计算线程已锁定

时间:2017-06-13 20:09:19

标签: c# .net multithreading

以下是代码:

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吗?

2 个答案:

答案 0 :(得分:1)

您需要确保以原子方式应用操作,并且不缓存值。要实现第一个,您必须使用Interlocked.IncrementInterlocked.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);
}