InvalidOperationException:调用线程无法访问此对象,因为另一个线程拥有它。

可能重复:
调用线程无法访问此对象,因为另一个线程拥有它

错误:

The calling thread cannot access this object because a different thread owns it. 

码:

 public partial class MainWindow : Window { Thread t; bool interrupt; public MainWindow() { InitializeComponent(); } private void btss_Click(object sender, RoutedEventArgs e) { if (t == null) { t = new Thread(this.calculate); t.Start(); btss.Content = "Stop"; } else { t.Interrupt(); } } private void calculate() { int currval = 2; int devide = 2; while (!interrupt) { for (int i = 2; i < currval/2; i++) { if (2 % i != 0) { lbPrimes.Items.Add(currval.ToString()); //Error occures here } } currval++; } } } 

会导致这种情况的原因,我该如何解决?

您需要重新加入主UI线程才能影响UI。 您可以使用InvokeRequired检查是否需要这样做 ,并在引用控件之前实现Invoke。

 private void calculate() { if (InvokeRequired) { Invoke(new Action(() => calculate())); } else { // } } 

lblPrimes从非UI线程访问任何UI元素(此处为lblPrimes )。 您必须使用线程中的Invoke来执行此操作。

这是一个很好的教程:

http://weblogs.asp.net/justin_rogers/pages/126345.aspx

您只能从主线程更新GUI。

在您的worker方法(calculate())中,您正尝试将项添加到列表框中。

 lbPrimes.Items.Add(currval.ToString()); 

这会导致exception。

您正在以非线程安全的方式访问控件。 当一个没有创建控件的线程试图调用它时,你将得到一个InvalidOperationException。

如果要将项添加到列表框,则需要使用InvokeRequired作为TheCodeKing提及。

例如:

 private delegate void AddListItem(string item); private void AddListBoxItem(string item) { if (this.lbPrimes.InvokeRequired) { AddListItem d = new AddListItem(item); this.Invoke(d, new object[] { item}); } else { this.lbPrimes.Items.Add(item); } } 

在Calculate()方法中调用此AddListBoxItem(…)方法,而不是直接尝试将项添加到列表框控件。

问题是您的工作线程正在尝试访问不允许的UI元素。 您获得的例外情况是警告您。 很多时候你甚至都没有。 相反,您的应用程序将无法预测和惊人地失败。

您可以使用Control.Invoke将委托的执行Control.Invoke送到UI线程上。 该委托将执行lbPrimes.Items.Add操作。 但是,在这种情况下我不推荐这种方法。 原因是因为它会减慢工作线程的速度。

我首选的解决方案是让工作线程将currval添加到ConcurrentQueue 。 然后,UI线程将通过System.Windows.Forms.Timer定期轮询此集合,以使值出列并将它们放在ListBox 。 与使用Control.Invoke相比,这有很多优点。

  • 它消除了Invoke强加的worker和UI线程之间的紧密耦合。
  • 它将更新UI的责任放在它应该属于的UI线程中。
  • UI线程决定更新发生的时间和频率。
  • 工作线程不必等待UI响应Invoke请求。 它将增加工作线程的吞吐量。
  • 由于Invoke操作成本高,因此效率更高。
  • 尝试使用Invoke终止工作线程时出现的许多微妙的竞争条件自然消失了。

以下是我首选的选项。

 private void calculate() { int currval = 2; int devide = 2; while (!interrupt) { for (int i = 2; i < currval/2; i++) { if (2 % i != 0) { queue.Add(currval); // ConcurrentQueue } } currval++; } } private void Timer_Tick(object sender, EventArgs args) { int value; while (queue.TryDequeue(out value)) { lbPrimes.Items.Add(value.ToString()); } } 

我注意到了其他一些问题。

  • Thread.Interrupt阻止BCL等待的调用,如WaitOneJoinSleep等。您对它的使用没有任何意义。 我想你要做的是设置interrupt = true
  • 你可能应该在for循环而不是while循环interrupt 。 如果currval变得足够大,则线程需要更长的时间来响应中断请求。