以下是代码:
private int _count = 0;
public bool Ongoing
{
get
{
return _count > 0;
}
}
public void Method1(object param)
{
new Thread(new ParameterizedThreadStart(Method2)).Start(param);
}
private void Method2(object param)
{
_count++;
lock (_lock)
{
// Something
}
_count--;
}
您可以猜测,变量_count用于计算锁定的线程数。它被初始化为0并且只在这个方法中被修改,我用它来知道类是否正在做某事。
现在问题是:有时_count低于0.它像_count ++有时会被忽略。
这种情况很少发生,比如大约每500次我开始使用这种方法,甚至更少。
我应该将_count声明为volatile吗?
答案 0 :(得分:1)
您需要确保以原子方式应用操作,并且不缓存值。要实现第一个,您必须使用Interlocked.Increment
和Interlocked.Decrement
,第二个标记为volatile
字段:
private volatile int _count = 0;
public bool Ongoing
{
get
{
return _count > 0;
}
}
public void Method1(object param)
{
new Thread(new ParameterizedThreadStart(Method2)).Start(param);
}
private void Method2(object param)
{
Interlocked.Increment(ref _count);
lock (_lock)
{
// Something
}
Interlocked.Decrement(ref _count);
}
答案 1 :(得分:1)
正如在接受的答案中正确指出的那样,您需要围绕递增和递减操作执行线程同步。这可以通过Interlocked
类来实现。
但是,您还需要使用一些内存同步机制来确保其他线程可以使用最新值。 volatile
上的MSDN文档非常错误:
MSDN文档指出使用volatile关键字可确保始终在字段中显示最新值。这是不正确的,因为正如我们所见,可以重新排序读取后跟读取。 (Joseph Albahari)
具体来说,volatile关键字指示编译器在每次从该字段读取时生成获取栅栏。但是,在读取之后,acquire-fence会生效,以防止在其之前移动其他读/写。这意味着可以向上移动读取本身,允许在第一次读取变量时检索过时值。
不幸的是,没有干净的方法来读取具有所需内存障碍的整数,但最近的候选者是冗余的Interlocked.CompareExchange
(参见this answer):
private int _count = 0;
public bool Ongoing => Interlocked.CompareExchange(ref _count, 0, 0) > 0;
public void Method1(object param)
{
new Thread(new ParameterizedThreadStart(Method2)).Start(param);
}
private void Method2(object param)
{
Interlocked.Increment(ref _count);
lock (_lock)
{
// Something
}
Interlocked.Decrement(ref _count);
}