class ThreadSyncDemo
{
static volatile int sum = 0; // 静态变量
static void foo1()
{
for (int i = 0; i < 1000000000; ++i) // 10亿次
{
sum++;
}
Console.WriteLine("foo1() completed, sum is " + sum);
}
static void foo2()
{
for (int i = 0; i < 1000000000; ++i) // 10亿次
{
sum++;
}
Console.WriteLine("foo2() completed, sum is " + sum);
}
// 主函数
static void Main()
{ Thread t1 = new Thread(foo1);
t1.Start();
Thread t2 = new Thread(foo2);
t2.Start();
Console.ReadLine();
}
} 大家在自己的电脑上编译运行上面的代码,最后结果是否是20亿???(我的CPU是T9300 双核)
foo1() completed, sum is 1008290573
foo2() completed, sum is 1037423262
请按任意键继续. . . 能否说说原因
{
static volatile int sum = 0; // 静态变量
static void foo1()
{
for (int i = 0; i < 1000000000; ++i) // 10亿次
{
sum++;
}
Console.WriteLine("foo1() completed, sum is " + sum);
}
static void foo2()
{
for (int i = 0; i < 1000000000; ++i) // 10亿次
{
sum++;
}
Console.WriteLine("foo2() completed, sum is " + sum);
}
// 主函数
static void Main()
{ Thread t1 = new Thread(foo1);
t1.Start();
Thread t2 = new Thread(foo2);
t2.Start();
Console.ReadLine();
}
} 大家在自己的电脑上编译运行上面的代码,最后结果是否是20亿???(我的CPU是T9300 双核)
foo1() completed, sum is 1008290573
foo2() completed, sum is 1037423262
请按任意键继续. . . 能否说说原因
foo1() completed, sum is 1971071770
foo2() completed, sum is 2000000000
foo2() completed, sum is 1235668282
但是,CLI使用的是纯堆栈的用,所以就算使用vol关键字,你的sum还是得先入栈,就因为这一步,你foo1刚入栈,切换到foo2运行,也入栈并完成计算,这样foo1,foo2假使读取的都是1000,经过foo2运算sum是1001,但是你foo1接着刚才运算还是1001,这样虽然foo1和2都对sum加1,最后内存里的sum不是1002,依旧是1001,这就是为啥有时候达不到20亿的原因.
如果和单线程一样,是2亿,就验证了我的观点。
http://topic.csdn.net/u/20080821/19/C522CAC6-89C7-434A-92C5-390B4289C889.html
竞态(racing)是最难重现和调试的。另:严格的说,单核的机器也会出现上述问题(如果线程切换正好发生在特殊点上)。不过这种运气实在是太稀少了。
实际上,我也很疑惑,volatile到底什么情况下用的到。struct SpinLock
{
private volatile int token=0; public void Enter()
{
while(Interlocked.Exchange(ref token, 1)!=0) // spin
} public void Exit()
{
token=0;
}
}
sum++等同于sun=sum+1,既要先读后加最后赋值三步操作。
volatile的作用是不保证原子性也不保证顺序性,而是把数据放入内存中,故两函数读该内存数据时不能保证值为最新值。
例:内存数据值为10,函1读取10,函数2加1后为11,此时内存为11,同时函1还是10,相加后又将数据赋给内存11.