我最近学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变量产生了争用的,谁能来具体分析下这个过程?.NETC#同步

解决方案 »

  1.   

    因为+=操作不是原子化的,可以被打断。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。