C# 使用连锁使用

C# 使用连锁使用,c#,multithreading,C#,Multithreading,我试图编写一个多线程程序,其中每个线程将使用一个计数器,然后递增它 例如: lock(this) { counter++; Console.WriteLine(counter); } 我知道对于增量,我可以使用: System.Threading.Interlocked.Increment(counter); 但是,对于递增和使用计数器执行操作,如何锁定呢 谢谢 您需要使用锁来保护读写。在这种情况下,lock语句工作得最好,并且最容易遵循: private int counte

我试图编写一个多线程程序,其中每个线程将使用一个计数器,然后递增它

例如:

lock(this)
{
   counter++;
   Console.WriteLine(counter); 
}
我知道对于增量,我可以使用:

System.Threading.Interlocked.Increment(counter);
但是,对于递增和使用计数器执行操作,如何锁定呢


谢谢

您需要使用锁来保护读写。在这种情况下,
lock
语句工作得最好,并且最容易遵循:

private int counter;
private readonly object locker = new object();

public void IncrementCounter()
{
    lock (this.locker)
    {
       this.counter++;
       Console.WriteLine(counter); 
    }
}

public int GetCounter()
{
    lock (this.locker)
    {
       return this.counter;
    }
}

所有联锁函数在修改后返回一个值的副本,在线程期间使用该值

var localCounter = System.Threading.Interlock.Increment(counter);

interlocted.Increment
函数既增加计数器的值,又返回其值;这两个操作保证是原子的。存在用于递减/读取计数器或添加/读取变量的其他联锁功能。通常,可以通过以下模式对Int32或Int64执行几乎任何简单的操作:

Int64 oldValue, newValue;
do
{
  oldValue = theVariable;
  newValue = oldValue | 1;  // Sample operation
} while (Interlocked.CompareExchange(theVariable, newValue, oldValue) != oldValue);
我们必须小心地获得正确的模式(例如,确保
newValue
是根据
oldValue
计算的,而不是
变量
),但模式非常简单且通用。

这样做可以:

线程A:

var incremented_counter = Interlocked.Increment(ref counter);
Console.WriteLine(incremented_counter);
Interlocked.Increment(ref counter);
lock (the_lock) {
   ++counter;
   Console.WriteLine(counter); 
}
lock (the_lock) {
   ++counter;
}
lock (the_lock) {
    var incremented_counter = Interlocked.Increment(ref counter);
    Console.WriteLine(incremented_counter);
}
lock (the_lock) {
    Interlocked.Increment(ref counter);
}
Interlocked.Increment(ref counter);
Console.WriteLine(counter);
Interlocked.Increment(ref counter);
lock (the_lock) {
   ++counter;
   Console.WriteLine(counter); 
}
Interlocked.Increment(ref counter);
var incremented_counter = Interlocked.Increment(ref counter);
Console.WriteLine(incremented_counter);
lock (the_lock) {
   ++counter;
}
线程B:

var incremented_counter = Interlocked.Increment(ref counter);
Console.WriteLine(incremented_counter);
Interlocked.Increment(ref counter);
lock (the_lock) {
   ++counter;
   Console.WriteLine(counter); 
}
lock (the_lock) {
   ++counter;
}
lock (the_lock) {
    var incremented_counter = Interlocked.Increment(ref counter);
    Console.WriteLine(incremented_counter);
}
lock (the_lock) {
    Interlocked.Increment(ref counter);
}
Interlocked.Increment(ref counter);
Console.WriteLine(counter);
Interlocked.Increment(ref counter);
lock (the_lock) {
   ++counter;
   Console.WriteLine(counter); 
}
Interlocked.Increment(ref counter);
var incremented_counter = Interlocked.Increment(ref counter);
Console.WriteLine(incremented_counter);
lock (the_lock) {
   ++counter;
}

这样做是可以的:

线程A:

var incremented_counter = Interlocked.Increment(ref counter);
Console.WriteLine(incremented_counter);
Interlocked.Increment(ref counter);
lock (the_lock) {
   ++counter;
   Console.WriteLine(counter); 
}
lock (the_lock) {
   ++counter;
}
lock (the_lock) {
    var incremented_counter = Interlocked.Increment(ref counter);
    Console.WriteLine(incremented_counter);
}
lock (the_lock) {
    Interlocked.Increment(ref counter);
}
Interlocked.Increment(ref counter);
Console.WriteLine(counter);
Interlocked.Increment(ref counter);
lock (the_lock) {
   ++counter;
   Console.WriteLine(counter); 
}
Interlocked.Increment(ref counter);
var incremented_counter = Interlocked.Increment(ref counter);
Console.WriteLine(incremented_counter);
lock (the_lock) {
   ++counter;
}
线程B:

var incremented_counter = Interlocked.Increment(ref counter);
Console.WriteLine(incremented_counter);
Interlocked.Increment(ref counter);
lock (the_lock) {
   ++counter;
   Console.WriteLine(counter); 
}
lock (the_lock) {
   ++counter;
}
lock (the_lock) {
    var incremented_counter = Interlocked.Increment(ref counter);
    Console.WriteLine(incremented_counter);
}
lock (the_lock) {
    Interlocked.Increment(ref counter);
}
Interlocked.Increment(ref counter);
Console.WriteLine(counter);
Interlocked.Increment(ref counter);
lock (the_lock) {
   ++counter;
   Console.WriteLine(counter); 
}
Interlocked.Increment(ref counter);
var incremented_counter = Interlocked.Increment(ref counter);
Console.WriteLine(incremented_counter);
lock (the_lock) {
   ++counter;
}

这样做可以,但多余:

线程A:

var incremented_counter = Interlocked.Increment(ref counter);
Console.WriteLine(incremented_counter);
Interlocked.Increment(ref counter);
lock (the_lock) {
   ++counter;
   Console.WriteLine(counter); 
}
lock (the_lock) {
   ++counter;
}
lock (the_lock) {
    var incremented_counter = Interlocked.Increment(ref counter);
    Console.WriteLine(incremented_counter);
}
lock (the_lock) {
    Interlocked.Increment(ref counter);
}
Interlocked.Increment(ref counter);
Console.WriteLine(counter);
Interlocked.Increment(ref counter);
lock (the_lock) {
   ++counter;
   Console.WriteLine(counter); 
}
Interlocked.Increment(ref counter);
var incremented_counter = Interlocked.Increment(ref counter);
Console.WriteLine(incremented_counter);
lock (the_lock) {
   ++counter;
}
线程B:

var incremented_counter = Interlocked.Increment(ref counter);
Console.WriteLine(incremented_counter);
Interlocked.Increment(ref counter);
lock (the_lock) {
   ++counter;
   Console.WriteLine(counter); 
}
lock (the_lock) {
   ++counter;
}
lock (the_lock) {
    var incremented_counter = Interlocked.Increment(ref counter);
    Console.WriteLine(incremented_counter);
}
lock (the_lock) {
    Interlocked.Increment(ref counter);
}
Interlocked.Increment(ref counter);
Console.WriteLine(counter);
Interlocked.Increment(ref counter);
lock (the_lock) {
   ++counter;
   Console.WriteLine(counter); 
}
Interlocked.Increment(ref counter);
var incremented_counter = Interlocked.Increment(ref counter);
Console.WriteLine(incremented_counter);
lock (the_lock) {
   ++counter;
}

但这样做是不行的:

线程A:

var incremented_counter = Interlocked.Increment(ref counter);
Console.WriteLine(incremented_counter);
Interlocked.Increment(ref counter);
lock (the_lock) {
   ++counter;
   Console.WriteLine(counter); 
}
lock (the_lock) {
   ++counter;
}
lock (the_lock) {
    var incremented_counter = Interlocked.Increment(ref counter);
    Console.WriteLine(incremented_counter);
}
lock (the_lock) {
    Interlocked.Increment(ref counter);
}
Interlocked.Increment(ref counter);
Console.WriteLine(counter);
Interlocked.Increment(ref counter);
lock (the_lock) {
   ++counter;
   Console.WriteLine(counter); 
}
Interlocked.Increment(ref counter);
var incremented_counter = Interlocked.Increment(ref counter);
Console.WriteLine(incremented_counter);
lock (the_lock) {
   ++counter;
}
线程B:

var incremented_counter = Interlocked.Increment(ref counter);
Console.WriteLine(incremented_counter);
Interlocked.Increment(ref counter);
lock (the_lock) {
   ++counter;
   Console.WriteLine(counter); 
}
lock (the_lock) {
   ++counter;
}
lock (the_lock) {
    var incremented_counter = Interlocked.Increment(ref counter);
    Console.WriteLine(incremented_counter);
}
lock (the_lock) {
    Interlocked.Increment(ref counter);
}
Interlocked.Increment(ref counter);
Console.WriteLine(counter);
Interlocked.Increment(ref counter);
lock (the_lock) {
   ++counter;
   Console.WriteLine(counter); 
}
Interlocked.Increment(ref counter);
var incremented_counter = Interlocked.Increment(ref counter);
Console.WriteLine(incremented_counter);
lock (the_lock) {
   ++counter;
}

它也没有这样做:

线程A:

var incremented_counter = Interlocked.Increment(ref counter);
Console.WriteLine(incremented_counter);
Interlocked.Increment(ref counter);
lock (the_lock) {
   ++counter;
   Console.WriteLine(counter); 
}
lock (the_lock) {
   ++counter;
}
lock (the_lock) {
    var incremented_counter = Interlocked.Increment(ref counter);
    Console.WriteLine(incremented_counter);
}
lock (the_lock) {
    Interlocked.Increment(ref counter);
}
Interlocked.Increment(ref counter);
Console.WriteLine(counter);
Interlocked.Increment(ref counter);
lock (the_lock) {
   ++counter;
   Console.WriteLine(counter); 
}
Interlocked.Increment(ref counter);
var incremented_counter = Interlocked.Increment(ref counter);
Console.WriteLine(incremented_counter);
lock (the_lock) {
   ++counter;
}
线程B:

var incremented_counter = Interlocked.Increment(ref counter);
Console.WriteLine(incremented_counter);
Interlocked.Increment(ref counter);
lock (the_lock) {
   ++counter;
   Console.WriteLine(counter); 
}
lock (the_lock) {
   ++counter;
}
lock (the_lock) {
    var incremented_counter = Interlocked.Increment(ref counter);
    Console.WriteLine(incremented_counter);
}
lock (the_lock) {
    Interlocked.Increment(ref counter);
}
Interlocked.Increment(ref counter);
Console.WriteLine(counter);
Interlocked.Increment(ref counter);
lock (the_lock) {
   ++counter;
   Console.WriteLine(counter); 
}
Interlocked.Increment(ref counter);
var incremented_counter = Interlocked.Increment(ref counter);
Console.WriteLine(incremented_counter);
lock (the_lock) {
   ++counter;
}

它也没有这样做:

线程A:

var incremented_counter = Interlocked.Increment(ref counter);
Console.WriteLine(incremented_counter);
Interlocked.Increment(ref counter);
lock (the_lock) {
   ++counter;
   Console.WriteLine(counter); 
}
lock (the_lock) {
   ++counter;
}
lock (the_lock) {
    var incremented_counter = Interlocked.Increment(ref counter);
    Console.WriteLine(incremented_counter);
}
lock (the_lock) {
    Interlocked.Increment(ref counter);
}
Interlocked.Increment(ref counter);
Console.WriteLine(counter);
Interlocked.Increment(ref counter);
lock (the_lock) {
   ++counter;
   Console.WriteLine(counter); 
}
Interlocked.Increment(ref counter);
var incremented_counter = Interlocked.Increment(ref counter);
Console.WriteLine(incremented_counter);
lock (the_lock) {
   ++counter;
}
线程B:

var incremented_counter = Interlocked.Increment(ref counter);
Console.WriteLine(incremented_counter);
Interlocked.Increment(ref counter);
lock (the_lock) {
   ++counter;
   Console.WriteLine(counter); 
}
lock (the_lock) {
   ++counter;
}
lock (the_lock) {
    var incremented_counter = Interlocked.Increment(ref counter);
    Console.WriteLine(incremented_counter);
}
lock (the_lock) {
    Interlocked.Increment(ref counter);
}
Interlocked.Increment(ref counter);
Console.WriteLine(counter);
Interlocked.Increment(ref counter);
lock (the_lock) {
   ++counter;
   Console.WriteLine(counter); 
}
Interlocked.Increment(ref counter);
var incremented_counter = Interlocked.Increment(ref counter);
Console.WriteLine(incremented_counter);
lock (the_lock) {
   ++counter;
}

(顺便说一句,)

关于这是一个控制台应用程序的问题,如果一些人试图将此处的建议移植到Windows应用程序,他们可能会发现一些惊喜,尤其是在锁定机制方面。我想说的是:

    object lock_obj = new object();

    private async void button1_Click(object sender, EventArgs e)
    {
        if (Monitor.TryEnter(lock_obj))
        {
            int t = await Task.Run(() => getVal());
            textBox1.Text = t.ToString(); ;
            Monitor.Exit(lock_obj);
        }
    }

    int count = 0;

    int getVal()
    {
    Thread.Sleep(1000);
    count++;
    return count;
    }
在上面的代码中,getval将休眠1秒。大多数人会认为Monitor.TryEnter会阻止在getval仍处于1秒睡眠状态时执行if语句中的代码块,但事实上,如果在1秒内多次按下该按钮,您将看到textBox1值递增,这可能不是我们希望从代码中得到的意图。但是,如果将联锁替换为监视器锁定,则行为会发生变化:

    int interlockCount=0;

    private async void button1_Click(object sender, EventArgs e)
    {
        if (Interlocked.CompareExchange(ref interlockCount, 1,0) == 0)
        {
            int t = await Task.Run(() => getVal());
            textBox1.Text = t.ToString();
            Interlocked.Exchange(ref interlockCount, 0);
        }
    }

您将看到,即使多次按下按钮,textBox1.Text也只会增加1(而getval仍处于1秒睡眠状态)。有效地阻止了if语句中的语句。

您遇到了什么具体问题?如果使用
lock
,则不需要Interlock.Increment。你的问题到底是什么?我指的是这篇文章:只要“使用计数器做某事”是只读的,这里就没有问题。@HenkHolterman这取决于,如果“只读”意味着所有线程都是只读的,那么你是对的,但是,如果计数器是在32位机器上运行的
,如果其他线程可以更新该值,则无法安全地读取它。这就是
互锁的原因。Exchange
互锁。CompareExchange
在修改前返回值(如果返回'after'值,它们将有点无用)。您是正确的。我想顺便说一句,我有时希望进行
互锁.PostIncrement
互锁.PostDecrement
操作,尤其是在vb中,它不是一个方便的“未检查的整数加减”操作。基本上,它们的操作与正常的
联锁操作相同。递增/递减
(以下是未经检查的减法/加法),但如果需要调整前变量的值,在调用中指定该值似乎比执行调整然后取消调整结果更符合逻辑。值的返回不能保证是原子的,只能更新传入的变量。如果另一个线程正在读取变量的值,而该变量的值是在赋值时从函数的返回中设置的,那么您将得到一个值。返回的值是非线程安全的本地副本。@ScottChamberlain:我99.99999%确定
Foo=Interlocked.Increment(Bar)
将执行一个原子序列[temp=Bar,temp+=1,Bar=temp],然后非原子地执行操作[Foo=temp]。返回值的生成保证是原子性的,但不能合理地期望
联锁的
函数保证返回后发生的任何事情的原子性。是的,但你的答案是“两者都增加计数器并返回其值;这两个操作保证是原子的。“对我来说,你在
Foo=Interlocked.Increment(Bar)
中暗示Foo的赋值是原子的。@ScottChamberlain:很抱歉有任何混淆。原始海报建议的序列本质上是“原子增量变量”;读取变量;该序列的前两个步骤本身可以原子化完成,即使第三个步骤不能-1
interlocated.Increment
获取一个参考参数,并在单个原子操作中读取、递增和存储该参数。
var incremented\u counter=Interlock.Increment(ref counter)
是正确的版本。@Aidiakapi这怎么会使我写的任何东西不正确?顺便说一句,我确实打错了:它应该是
互锁的
(而不是
互锁的
)。你的示例中没有一个会编译。@Aidiakapi啊…我忘了
参考的
(编辑).但其他人也犯了同样的错误,我看不出你对他们投了反对票!无论如何,这只是一个语法错误,与我想说的更重要的一点并不相关-你不同意我的观点吗