【问题标题】:Avoiding Cross-Thread Operations避免跨线程操作
【发布时间】:2015-04-27 18:41:51
【问题描述】:

我想从我的后台工作人员访问我的 GUI 上的列表框中的选择。如果没有任何其他更改尝试这样做会引发此错误

Cross-thread Operation Not Valid: Control '_ListBox1' accessed from a thread other than the thread it was created on

我看到的避免这种情况的选项是通过以下语法使用Invoke,但这是否可以接受.Net 4(或更高版本)?

var selectedItems = (IList)this.Invoke(new Func<IList>(() => Listbox1.SelectedItems.Cast<object>().ToList()));

为了更清晰的图片,这就是我想从我的后台工作人员访问列表框项目的方式

namespace clown
{
  public partial class Form1 : Form1
  {
    public Form1()
    {
      ListBox1.Items.Add("Firefly");
      ListBox1.Items.Add("Hellfire");
    }

    private void btn1234_Click()
    {
      backgroundworker1.RunWorkerAsync();
    }

    private void backgroundworker1_DoWork(object sender, DoWorkEventArgs e)
    {
      //Long Running Process taking place here 
      //then we hit this
      if (ListBox1.SelectedItems.Contains("Firefly")) { //take this course }
      if (ListBox1.SelectedItems.Contains("Hellfire)) { //take this course }
    }
   }
}

【问题讨论】:

  • .Net 4 可接受是什么意思?是指明智的标准还是明智的编译?
  • 你试过了吗?发生了什么?
  • “访问选择”是什么意思?您使用的是什么 API? ASP.NET?表格? WPF? Metro(或本周微软所称的任何东西)?
  • @Alex & Josh L.- 它有效,免费发行。只是不确定这样做是否“可以接受”。

标签: c# multithreading winforms


【解决方案1】:

Backgroundworker'sDoWork 事件处理程序中的Invoke 被微软拒绝。您应该考虑改用 Backgroundworker's ProgressChangedRunWorkerCompleted 事件。

MSDN Help Here

请参阅此文本的第一个注释:

您必须小心不要在其中操作任何用户界面对象 你的 DoWork 事件处理程序。相反,与用户界面进行通信 通过 ProgressChanged 和 RunWorkerCompleted 事件。

如果您实际发布了更多代码,可能会给出更完整的答案。

编辑 1:更多代码

自从 OP 更新了他的帖子以包含更多代码,我也做了同样的事情。

   private void btn1234_Click()
   {
        var items = ListBox1.SelectedItems.Cast<string>();
        backgroundWorker1.RunWorkerAsync(items);
    }

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        var worker = (BackgroundWorker)sender;
        var items = (IEnumerable<string>)e.Argument;
        //Long Running Process taking place here  then we hit this
        if (items.Contains("Firefly")) { /* take this course */ }
        if (items.Contains("Hellfire")) { /* take this course */ }
    }

【讨论】:

  • 谢谢你!我已经更新了我最初的帖子,以便更清楚地了解我正在努力实现的目标。
  • 我同意你的看法。我会考虑为工作人员提供 click 方法所需的所有信息 - 状态更新通过您描述的方法进行
  • @MarioTheSpoon 我如何将选定的项目传递给后台工作人员?
  • @user2676140 我提供了一个关于如何传递项目的示例。但我强烈建议您花更多时间阅读 MSDN 帮助并查看他们的示例。
  • @Rick Davin:感谢添加参数传递 ;-)
【解决方案2】:

这是“可以接受的”,但现在这不是一个好习惯。 BackgroundWorker 被认为是过时的,应该使用带有 async/await 的 Tasks。

这是一个示例,说明我将如何实现一种方法以在另一个线程上执行某些操作,保持 UI 响应并在执行完成后更新 UI(使用 WPF 和 MVVM 模式):

public class ViewModel : INotifyPropertyChanged
{
    ...

    public string PathInfo { ... } // raises INotifyPropertyChanged.PropertyChanged event from the setter
    public RelayCommand ProcessPathsCommand { get; set; }

    public ViewModel()
    {
        ProcessPathsCommand = new RelayCommand(ProcessPaths);
    }

    public async void ProcessPaths()
    {
        // disable the command, which will lead to disabling a button bound to the command
        ProcessPathsCommand.IsEnabled = false;

        try
        {
            string result = null;
            // run processing on another thread
            await Task.Run(() =>
            {
                // emulate hard-work
                Thread.Sleep(5000);
                result = "Here are the results: bla bla bla";
            });

            // update the property on the view model, which will lead to updating a textblock bound to this property                
            // thanks to the "await" keyword, this line of code will be executed only when the task finishes
            // and it will be executed on UI thread
            PathInfo = result;
        }
        finally
        {
            ProcessPathsCommand.IsEnabled = true;
        }
    }

如果您需要更多详细信息,请随时告诉我。

【讨论】:

  • 我正在使用一个winforms项目,与WPF相同的原则是否适用?
  • 嗯,我认为 MVVM 在 WinForms 开发人员中不是很流行,因为当 MVVM 流行时,WinForms 已经出现了好几年,所以有很多 WinForms 开发人员已经习惯了他们的旧方法。但是是的,同样的原则也适用于 WinForms。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-01-29
  • 2018-04-15
  • 2011-07-11
  • 1970-01-01
相关资源
最近更新 更多