静态变量的并发增量 g = g + 1 看起来是原子的,但不应该是?
Concurrent increment g = g + 1 of a static variable appears atomic, but shouldn't be?
无法找到为什么下面带有注释 Monitor
的代码在写入 g
时像同步一样工作并且总是 returns g=50
。我期待一些差异结果小于 50
。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace _7_monitor
{
class Program
{
static object sync = new object();
static int g = 0;
static Barrier b = new Barrier(6, (b) => { Console.WriteLine("barier reached"); } );
public static void tjob(object obj)
{
int t = (int)obj;
for (int i = 0; i < 10; i++)
{
//Monitor.Enter(sync);
g = g + 1;
//Monitor.Exit(sync);
Console.WriteLine("thr {0} iter={1}", t , i);
}
b.SignalAndWait();
}
static void Main(string[] args)
{
for (int i = 0; i < 5; i++)
{
Thread d = new Thread(tjob);
d.Start(i);
}
Console.WriteLine("waiting");
b.SignalAndWait();
Console.WriteLine("g={0}",g);
Console.ReadLine();
}
}
}
正如其他人指出的那样,一个 10 的循环会很快完成,以至于其他线程很可能还没有开始,所以您可能已经顺序访问了全局静态变量 g
,无论如何,因此观察到的结果是一致的。
在没有围绕 g
的保护的情况下,使用更长的循环(我去掉了一些多余的部分),我们确实得到了类似于随机数生成器的东西。
var threads = new List<Thread>();
for (var i = 0; i < 5; i++)
{
var d = new Thread(x => {
for (var loop = 0; loop < 100000; loop++)
{
// unsynchronized mutation
g = g + 1;
}
});
d.Start(i);
threads.Add(d);
}
foreach (var t in threads)
{
t.Join();
}
Console.WriteLine("g={0}", g); // 158609, 173331, 127983, ... (i7 with 4 HT Cores)
根据@Jurgis 的评论,Interlocked.Increment 已提供给 .Net
而不是 Monitor
或 lock
将 g = g + 1
替换为 Interlocked.Increment(ref g);
returns 预期:
g=500000
(显然,在现实世界中,并行化只会争用共享变量的工作绝对没有意义)
无法找到为什么下面带有注释 Monitor
的代码在写入 g
时像同步一样工作并且总是 returns g=50
。我期待一些差异结果小于 50
。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace _7_monitor
{
class Program
{
static object sync = new object();
static int g = 0;
static Barrier b = new Barrier(6, (b) => { Console.WriteLine("barier reached"); } );
public static void tjob(object obj)
{
int t = (int)obj;
for (int i = 0; i < 10; i++)
{
//Monitor.Enter(sync);
g = g + 1;
//Monitor.Exit(sync);
Console.WriteLine("thr {0} iter={1}", t , i);
}
b.SignalAndWait();
}
static void Main(string[] args)
{
for (int i = 0; i < 5; i++)
{
Thread d = new Thread(tjob);
d.Start(i);
}
Console.WriteLine("waiting");
b.SignalAndWait();
Console.WriteLine("g={0}",g);
Console.ReadLine();
}
}
}
正如其他人指出的那样,一个 10 的循环会很快完成,以至于其他线程很可能还没有开始,所以您可能已经顺序访问了全局静态变量 g
,无论如何,因此观察到的结果是一致的。
在没有围绕 g
的保护的情况下,使用更长的循环(我去掉了一些多余的部分),我们确实得到了类似于随机数生成器的东西。
var threads = new List<Thread>();
for (var i = 0; i < 5; i++)
{
var d = new Thread(x => {
for (var loop = 0; loop < 100000; loop++)
{
// unsynchronized mutation
g = g + 1;
}
});
d.Start(i);
threads.Add(d);
}
foreach (var t in threads)
{
t.Join();
}
Console.WriteLine("g={0}", g); // 158609, 173331, 127983, ... (i7 with 4 HT Cores)
根据@Jurgis 的评论,Interlocked.Increment 已提供给 .Net
而不是Monitor
或 lock
将 g = g + 1
替换为 Interlocked.Increment(ref g);
returns 预期:
g=500000
(显然,在现实世界中,并行化只会争用共享变量的工作绝对没有意义)