首页 > 代码库 > 多重影分身——C#中多线程的使用二(争抢共享资源)
多重影分身——C#中多线程的使用二(争抢共享资源)
只要服务器承受得了,我们可以开任意个线程同时工作以提高效率,然而
两个线程争抢资源可能导致数据混乱。
例如:
public class MyFood { public static int Last { get; set; } public MyFood() { Last = 500; } public void EatFood() { int foods = Last; Thread.Sleep(1000); Last = foods - 1; Console.WriteLine(Last); } public void EatMuchFood() { int foods = Last; Thread.Sleep(3000); Last = foods - 10; Console.WriteLine(Last); } }
这里定义了个MyFood类,里面有个静态变量Last,存储剩余的食物。然后用构造函数假设初始有500的食物。
两个方法,分别是吃一个食物和吃10个食物,用Thread.Sleep()模拟吃东西需要花费时间。
OK,现在开始吃东西了:
MyFood e = new MyFood ();Thread th1 = new Thread(new ThreadStart(e.EatFood));Thread th2 = new Thread(new ThreadStart(e.EatMuchFood));th1.Start(); //499 th1.Join(); //这里等待第一个线程结束,再往后面走th2.Start(); //489Console.ReadKey();
没有任何问题,吃完第一个食物花费1秒钟,剩余499个。然后花费3秒钟吃10个食物,剩余489个。
假如我们不加th1.Join(), 那就是正常情况下的多线程,看看会发生什么:
MyFood e = new MyFood ();Thread th1 = new Thread(new ThreadStart(e.EatFood));Thread th2 = new Thread(new ThreadStart(e.EatMuchFood));th1.Start(); //499th2.Start(); //490Console.ReadKey();
可以看到两次输出分别是499和490,这里就有问题了。
EatFood方法,取出剩余Last后休息了1秒,然后再把-1后的数字赋值给Last。恰好,在这1秒期间,我们的th2进来调用EatMuchFood方法了,它也是先取总数,而这个总数却是EatFood方法还没有-1的总数,于是它取到的是500而不是499.
这就是问题发生的原因。
那么在对待总数这种共享资源上,同一时间我们希望只有一个线程来访问(这样才能确保数据正确),其中一个解决方案就是Lock:
public class MyFood { private static readonly object lockHelper = new object(); public static int Last { get; set; } public MyFood() { Last = 500; } public void EatFood() { lock (lockHelper) { int foods = Last; Thread.Sleep(1000); Last = foods - 1; } Console.WriteLine(Last); } public void EatMuchFood() { lock (lockHelper) { int foods = Last; Thread.Sleep(3000); Last = foods - 10; } Console.WriteLine(Last); } }
这里添加了一个静态只读私有变量叫做lockHelper,然后修改了两个吃东西的方法,加了对lockHelper的锁定。
这样每次执行的时候,会判断lockHelper是否被锁定了,如果没有,就进入lock代码块把lockHelper锁定,并且执行其中的代码。出了lock代码块会自动释放锁。
这样就能保证在修改Last的时候,一次只有一个线程了。
本例中直接用lock(this)就行,this代表MyFood e 这个类的实例。
多重影分身——C#中多线程的使用二(争抢共享资源)