C# InvalidOperationException:调用线程无法访问此对象,因为其他线程拥有它。
可能重复:C# InvalidOperationException:调用线程无法访问此对象,因为其他线程拥有它。,c#,wpf,multithreading,C#,Wpf,Multithreading,可能重复: 错误: The calling thread cannot access this object because a different thread owns it. 代码: 公共部分类主窗口:窗口 { 螺纹t; 布尔中断; 公共主窗口() { 初始化组件(); } 专用无效BTS_单击(对象发送方,路由目标) { 如果(t==null) { t=新螺纹(此。计算); t、 Start(); btss.Content=“停止”; } 其他的 { t、 中断(); } } 私有无
错误:
The calling thread cannot access this object because a different thread owns it.
代码:
公共部分类主窗口:窗口
{
螺纹t;
布尔中断;
公共主窗口()
{
初始化组件();
}
专用无效BTS_单击(对象发送方,路由目标)
{
如果(t==null)
{
t=新螺纹(此。计算);
t、 Start();
btss.Content=“停止”;
}
其他的
{
t、 中断();
}
}
私有无效计算()
{
int currval=2;
int-devide=2;
而(!中断)
{
对于(int i=2;i
这是什么原因造成的?我如何解决它?您需要重新加入主UI线程才能影响UI。您可以检查是否需要,并在引用控件之前实现调用
private void calculate()
{
if (InvokeRequired)
{
Invoke(new Action(() => calculate()));
}
else
{
//
}
}
不允许从非UI线程访问任何UI元素(lblPrimes
)。您必须使用线程中的Invoke
来执行此操作
以下是一个很好的教程:
您只能从主线程更新GUI 在worker方法(calculate())中,您试图将项添加到列表框中
lbPrimes.Items.Add(currval.ToString());
这导致了异常
您正在以非线程安全的方式访问控件。当一个没有创建控件的线程尝试调用它时,您将得到一个InvalidOperationException
如果要将项目添加到列表框中,则需要使用前面提到的代码调用required
例如:
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(…)方法,而不是直接尝试将项添加到listbox控件。问题在于工作线程正在尝试访问不允许的UI元素。你得到的例外是警告你这一点。很多时候,你甚至都不明白。相反,您的应用程序将以不可预测和惊人的方式失败 您可以使用
Control.Invoke
将委托的执行封送到UI线程。此代理将执行lbPrimes.Items.Add
操作。但是,在这种情况下,我不推荐这种方法。原因是它会减慢工作线程的速度
我的首选解决方案是让工作线程将currval
添加到ConcurrentQueue
。然后,UI线程将通过System.Windows.Forms.Timer
定期轮询此集合,以将值出列并将其放入列表框中。与使用控件.Invoke相比,这有很多优点
- 它消除了
Invoke
施加的工作线程和UI线程之间的紧密耦合
- 它将更新UI的责任放在UI线程中,不管怎样,UI线程都应该属于该线程
- UI线程可以指定更新的时间和频率
- 工作线程不必等待UI响应
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());
}
}
private void calculate()
{
int currval=2;
int-devide=2;
而(!中断)
{
对于(int i=2;i
我注意到了另外两个问题
Thread.Interrupt
取消阻止BCL等待调用,如WaitOne
、Join
、Sleep
等。您使用它没有任何用途。我想你要做的是设置interrupt=true
- 您可能应该在
for
循环中中断,而不是while
循环。如果currval
变得足够大,线程响应中断请求的时间将越来越长
Calculate
不是这里的问题,访问lblPrimes
是。是的,确切地说是在Calculate中,因此调用返回到self。当然,你可以用不同的方法,把调用嵌套得更深一些,这只是一个例子。这是次优的,完全违背了使用线程的目标。您不希望在Invoke
中完成整个计算,只希望更新标签。使用你的代码,线程没有做任何事情。它实际上完全取决于你在做什么,正如我说的,目的是展示如何解决问题,而不是为OP编写最优化的生产准备代码。这是一个客户端应用程序,在大多数情况下,它真的没有丝毫区别。我认为这个解决方案更容易理解和维护。
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());
}
}