日期:2014-05-17  浏览次数:21192 次

C#中多个线程同时增加一未加锁共享变量的值,最后却不等于总增加值的问题
我最近学C#,看到《C#高级编程(第7版)》(Professoinal C# 4 and .NET 4)中20.9.1节(Lock 语句和线程安全)的一个内容,大概说:
同时运行20个相同的任务,这个任务会循环50000次每次给一个共享变量加1,这样,50000*20=1000000,最后这个变量将是1000000,但是事实上并非如此,代码贴到下面(控制台程序):

public class SharedState
{
public int State { get; set; }
}

public class Job
{
SharedState sharedState;
public Job(SharedState sharedState)
{
this.sharedState = sharedState;
}
public void DoTheJob()
{
for (int i = 0; i < 50000; i++)
{
sharedState.State += 1;
}
}
}

class Program
{
static void Main(string[] args)
{
int numTasks = 20;
var state = new SharedState();
var tasks = new Task[numTasks];
for (int i = 0; i < numTasks; i++)
{
tasks[i] = new Task(new Job(state).DoTheJob);
tasks[i].Start();
}
for (int i = 0; i < numTasks; i++)
tasks[i].Wait();
Console.WriteLine("summarized {0}",state.State);
Console.ReadLine();
}
}

多次运行的结果:
summarized 383286
summarized 481298
summarized 579142
......
每次运行结果都不一样,但都不等于1000000,书上说这代码很少很简单,很容易看出问题出在哪里,但没有具体说问题在哪里,我自己看了想了很久,都看不出问题出在哪里,也就是不清楚这20个任务在并行运行过程中,是怎么对sharedState.State变量产生了争用的,谁能来具体分析下这个过程?
.NET C# 同步

------解决方案--------------------
因为+=操作不是原子化的,可以被打断。

sharedState.State += 1;
相当于
x = sharedState.State  ...1
x = x + 1              ...2
sharedState.State = x  ...3
那么我们让线程1和线程2同时执行这个代码:
sharedState.State初始是0。
线程1执行前两行
此时sharedState.State还是0,x = 1
切换到线程2
线程2执行这3条代码,此时sharedState.State = 1
再切换回线程1,继续执行第三行,因为x是脏数据,它此时已经不代表线程2更新过的sharedState.State了
因此它仍然将sharedState.State设置为1。
我们看到两个线程执行了2次相加,结果还是1,而不是2。