C# 5.0 与 .Net 4.5 学习(一)Race Conditions资源竞争

时间:2022-11-03 03:17:25

Race Conditions资源竞争

如果两个或更多的线程通过同一对象并且共享状态不是同步的,一个资源竞争就可能发生。为了说明资源竞争,下面的例子定义了一个StateObject类,该类包含一个int类型字段和ChangeState方法。在ChangeState方法实现里面,这个状态变量根据是否为5来决定更改值;如果是,这个值自增。接下来的语句Trace.Assert将立即执行验证值是否为6。

为5的变量自增加1后,你可能断定这个变量现在是6,但是在这里它并不是重点。例如,如果一个线程执行了if(state ==5 )语句,调度者运行另一个线程,它可能优先占用资源。第二个线程现在走进了if语句体里面,并且由于这个state还是5这个值,state自增1变成6,第一个线程随后又被调度,并且下一次state自增变成7.这个时候就会发生资源竞争。

public class StateObject
{
  private int state = 5;
  public void ChangeState(int loop)
  {
    if (state == 5)
    {
      state++;
      Trace.Assert(state == 6, "Race condition occurred after " +
      loop + " loops");
    }
    state = 5;
  }
}

你可以为task定义一个的方法。SampleTask类里面的RaceCondition方法获得了一个StateObject作为参数。里面有一个while循环,ChangeState方法被调用。变量i是用来在信息中显示循环次数。

public class SampleTask
{
  public void RaceCondition(object o)
  {
    Trace.Assert(o is StateObject, "o must be of type StateObject");
    StateObject state = o as StateObject;
    int i = 0;
    while (true)
    {
      state.ChangeState(i++);
    }
  }
}

在程序的Main方法里面,一个新的StateObject被创建,它被所有的tasks所共享。通过使用Lambda表达式调用RaceConditions方法创建Task对象。主线程等待用户的输入。然而,这里可能因为资源竞争的发生使得在读取用户输入之前程序停止。

static void RaceConditions()
{
  var state = new StateObject();
  for (int i = 0; i < 2; i++)
  {
    Task.Run(() => new SampleTask().RaceCondition(state));
  }
}

 

 

C# 5.0 与 .Net 4.5 学习(一)Race Conditions资源竞争

 

你可以通过锁住共享的对象来避免这个问题。通过lock锁住被线程所共享的变量state,如下面例子。只有一个线程可以在lock块里面。因为对象被所有线程之间共享,如果一个线程拿到state的锁,另一个线程必须等待。当锁可用,这个线程拥有锁直到lock块的结束。如果每个线程通过锁改变这个对象,资源竞争就不会再发生:

public class SampleTask
{
  public void RaceCondition(object o)
  {
    Trace.Assert(o is StateObject, "o must be of type StateObject");
    StateObject state = o as StateObject;
    int i = 0;
    while (true)
    {
      lock (state) // no race condition with this lock
      {
        state.ChangeState(i++);
      }
    }
  }
}

 

当使用共享对象时,可以使共享对象线程安全,而不是使用共享对象执行锁。下面的代码,ChangeState方法包含了一个锁。你不能锁state变量本身(只有引用类型才能用于锁定),这个变量类型对象是通过定义和使用lock来同步。如果state值改变的时候加上一个每次都引用同样的同步对象的锁,资源竞争就不会发生: 

public class StateObject
{
  private int state = 5;
  private object sync = new object();
  public void ChangeState(int loop)
  {
    lock (sync)
    {
    if (state == 5)
    {
      state++;
      Trace.Assert(state == 6, "Race condition occurred after " +
      loop + " loops");
    }
    state = 5;
    }
  }
}

 

ps:未完,内容是看英文版自己翻译的,非原创,勿喷。无聊中...