可能重复:调用线程无法访问此对象,因为另一个线程拥有它
错误:
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++;
}
}
}
是什么导致这种情况,我该如何解决?
4 回答
您需要重新加入主UI线程才能影响UI . 您可以使用InvokeRequired检查是否需要这样做,并在引用控件之前实现Invoke .
不允许从非UI线程访问任何UI元素(此处为
lblPrimes
) . 您必须使用线程中的Invoke
来执行此操作 .这是一个很好的教程:
http://weblogs.asp.net/justin_rogers/pages/126345.aspx
您只能从主线程更新GUI .
在您的worker方法(calculate())中,您尝试将项目添加到列表框中 .
这会导致异常 .
您正在以非线程安全的方式访问控件 . 当没有创建控件的线程试图调用它时,您将获得InvalidOperationException .
如果要将项添加到列表框,则需要使用InvokeRequired作为TheCodeKing提及 .
例如:
在Calculate()方法中调用此AddListBoxItem(...)方法,而不是直接尝试将项添加到列表框控件 .
问题是您的工作线程正在尝试访问不允许的UI元素 . 您获得的例外情况是警告您 . 很多时候你甚至都没有 . 相反,您的应用程序将无法预测和惊人地失败 .
您可以使用
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
终止工作线程时出现的许多微妙的竞争条件自然消失了 .以下是我首选的选项 .
我注意到了其他一些问题 .
Thread.Interrupt
取消阻止BCL等待调用,如WaitOne
,Join
,Sleep
等 . 您对它的使用没有任何意义 . 我想你要做的是设置interrupt = true
.你应该在
for
循环中interrupt
而不是while
循环 . 如果currval
变得足够大,则线程需要更长的时间来响应中断请求 .