首页 > 代码库 > 多重影分身——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#中多线程的使用二(争抢共享资源)