可能重复:
The calling thread cannot access this object because a different thread owns it
错误:
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++;
}
}
}
导致这种情况的原因,我该如何解决?
答案 0 :(得分:4)
您需要重新加入主UI线程才能影响UI。您可以使用InvokeRequired检查是否需要这样做,并在引用控件之前实现Invoke。
private void calculate()
{
if (InvokeRequired)
{
Invoke(new Action(() => calculate()));
}
else
{
//
}
}
答案 1 :(得分:2)
不允许从非UI线程访问任何UI元素(此处为lblPrimes
)。您必须使用线程中的Invoke
来执行此操作。
这是一个很好的教程:
答案 2 :(得分:1)
您只能从主线程更新GUI。
在您的工人方法(calculate())中,您正尝试将项目添加到列表框中。
lbPrimes.Items.Add(currval.ToString());
这会导致异常。
您正在以非线程安全的方式访问控件。当一个没有创建控件的线程试图调用它时,你将得到一个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(...)方法,而不是直接尝试将项目添加到列表框控件。
答案 3 :(得分:0)
问题是您的工作线程正在尝试访问不允许的UI元素。您获得的例外情况是警告您。很多时候你甚至都没有。相反,您的应用程序将无法预测且非常惊人地失败。
您可以使用Control.Invoke
将委托的执行封送到UI线程上。该代表将执行lbPrimes.Items.Add
操作。但是,在这种情况下我不推荐这种方法。原因是它会减慢工作线程。
我首选的解决方案是让工作线程将currval
添加到ConcurrentQueue
。然后,UI线程将通过System.Windows.Forms.Timer
定期轮询此集合,以使值出列并将它们放在ListBox
中。与使用Control.Invoke
相比,这有很多优势。
Invoke
强加的工作线程和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<int>
}
}
currval++;
}
}
private void Timer_Tick(object sender, EventArgs args)
{
int value;
while (queue.TryDequeue(out value))
{
lbPrimes.Items.Add(value.ToString());
}
}
我注意到了其他一些问题。
Thread.Interrupt
取消阻止等待来电的BCL,例如WaitOne
,Join
,Sleep
等。您对它的使用毫无意义。我认为您要做的是设置interrupt = true
。interrupt
循环中for
而不是while
循环。如果currval
变得足够大,则线程需要更长的时间来响应中断请求。