我遇到了多线程问题。
我有一个方法被调用来对几个项目进行刷新。
在这个方法中,我迭代一个项目列表并刷新其中一个属性。
列表中有很多元素,我们必须做一些数学运算来计算它的属性。
此操作的当前代码如下所示:
public void AddItemsWithLayoutRefresh(IEnumerable<MyItem> items){
_control.Invoke(()=>{
AddItems(items);
for(int i =0;i<_guiItems.Count;i++){
//The goal is to have a condition here to "break" the loop and let the next call to RefreshLayout proceed
_guiItems[i].Propriety = ComputePropriety(_guiItems[i]);
}
});
}
问题是我可能有4个电话,目前正在阻止Invoke
。
我要完成“AddItems”方法,但是关于“for”循环中的所有内容,如果我知道它将在之后执行,我可以毫无问题地中止它。
但是如何以线程安全的方式做到这一点?
如果我输入private bool _isNewRefreshHere;
,在进入Invoke之前设置为true,然后检查Invoke
,我不保证在我之前还没有两个调用已经到达Invoke在for循环中检查它。
那么当我对我的方法进行新的调用时,如何在循环中时break
?
解决方案 根据Andrej Mohar的回答,我做了以下几点:
private long m_refreshQueryCount;
public void AddItemsWithLayoutRefresh(IEnumerable<MyItem> items){
Interlocked.Increment(ref m_refreshQueryCount);
_control.Invoke(()=>{
Interlocked.Decrement(ref m_refreshQueryCount);
AddItems(items);
for(int i =0;i<_guiItems.Count;i++){
if (Interlocked.Read(ref m_refreshQueryCount) > 0)
{
break;
}
_guiItems[i].Propriety = ComputePropriety(_guiItems[i]);
}
});
}
这似乎非常好用
答案 0 :(得分:1)
我会按以下方式进行:
private readonly object _refresherLock = new object();
private bool _isNewRefreshHere = false;
private AutoResetEvent _refresher = new AutoResetEvent(true);
public void AddItemsWithLayoutRefresh(IEnumerable<MyItem> items)
{
lock (_refresherLock)
{
if (_isNewRefreshHere)
{
return;
}
_isNewRefreshHere = true;
}
_refresher.WaitOne();
_isNewRefreshHere = false;
_control.Invoke(() =>
{
AddItems(items);
for (int i = 0; i < _guiItems.Count && !_isNewRefreshHere; i++)
{
_guiItems[i].Propriety = ComputePropriety(_guiItems[i]);
}
_refresher.Set();
});
}
那是:
答案 1 :(得分:1)
如果我是你,我会尝试制作一个线程安全的等待计数器。您可以使用增量和减量等Interlocked方法。这些基本上做的是它们将值增加为原子操作,这被认为是线程安全的。所以你在Invoke调用之前增加变量。这将允许您知道等待队列中有多少个线程。在for循环结束后和Invoke块结束之前递减变量。然后,您可以在for语句中检查等待线程的数量,如果数字大于1则中断for。这样您就应该知道执行链中确切的线程数。