C#多线程开发:使用lock语句同步多个线程

本文通过一个示例说明了在C#中,未进行同步操作的多线程访问共享数据可能导致数据不一致的问题,并介绍了如何使用lock语句解决这个问题,确保每次只有一个线程能访问和修改共享数据。

在多个线程之间共享数据时,需要考虑线程同步问题,必须确保每次只有一个线程访问和改变共享数据。

C#中使用lock语句可以轻松地设置和解除锁定以期达到每次只有一个线程访问和改变共享数据的目的。

下面是一个多线程访问共享数据的实例,看看在没有进行同步操作的情况下会出现什么样的问题?

[csharp]  view plain  copy
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Text;  
  5. using System.Threading.Tasks;  
  6. using System.Threading;  
  7. namespace Invok  
  8. {  
  9.     class Program  
  10.     {  
  11.         static int account = 1000;//账户    
  12.         static int pocket = 0;//口袋   
  13.         static void Main(string[] args)  
  14.         {  
  15.             int threadCount = 10;    
  16.             var threads = new Thread[threadCount];    
  17.             for (int i = 0; i < threadCount; i++)    
  18.             {    
  19.                 threads[i] = new Thread(DoWork);    
  20.                 threads[i].Start();    
  21.             }    
  22.             for (int i = 0; i < threadCount; i++)    
  23.             {    
  24.                 threads[i].Join();    
  25.             }    
  26.             Console.WriteLine("pocket=" + pocket);  
  27.             Console.ReadLine();  
  28.         }  
  29.         public static void DoWork()    
  30.         {  
  31.             if (account >= 1000)    
  32.             {    
  33.                 Thread.Sleep(10);//自动取款机打了个小盹    
  34.                 account -= 1000;    
  35.                 pocket += 1000;    
  36.             }   
  37.         }       
  38.     }  
  39. }  

可以将示例代码理解成:一个用户分十次从自己的银行账户中取钱。

取钱的逻辑由下面的代码来实现。

[csharp]  view plain  copy
  1. if (account >= 1000)    
  2.  {    
  3.       <span style="white-space:pre">    </span>Thread.Sleep(10);//自动取款机打了个小盹    
  4. <span style="white-space:pre">  </span>account -= 1000;    
  5.         pocket += 1000;    
  6. }   

当账户中的余额大于等于1000时,就取出1000放进自己的口袋。

因为用户当前的账户中仅剩下1000,所以就算用户取了10次,最终口袋中也应该只有1000。那么实际情况又是怎样的呢?

请看下面的执行结果(结果也可能是1000,2000,...,9000中的一个)。

结果竟然是10000!!!!!!!!

用户从仅有1000余额的账户中取出了10000,实在是一件令人振奋人心的事。

不过对于银行来说,这可不是件什么好事,因为照这样下去,银行的钱迟早会被用户掏空。

为什么会出现这样的结果呢?

这是因为没有对多线程访问共享数据进行同步,10个线程同时进入了取钱的逻辑,所以一共取出了10000。

为了解决这个问题,可以使用lock语句来同步多线程访问共享数据。下面是增加lock语句后的取钱逻辑。

[csharp]  view plain  copy
  1. static readonly object locker = new object();     
  2. public static void DoWork()    
  3. {  
  4. <span style="white-space:pre">  </span>lock (locker)  
  5.         {  
  6.             if (account >= 1000)    
  7.             {    
  8.                 Thread.Sleep(10);//自动取款机打了个小盹    
  9.                 account -= 1000;    
  10.                 pocket += 1000;    
  11.             }   
  12.         }  
  13. }      
代码中使用lock关键字锁定对象o,当一个线程获得锁定后,其他线程就无法再获得锁定,只有当当前线程解除锁定后,其他对象才可以重新获得锁定,这样一来,就可以保证每次只有一个线程获得锁定进而访问和修改共享数据。

多次执行修改后的示例代码,每次都可以得到以下的正确结果。


lock语句锁定的对象,必须是引用类型。因为锁定值类型只是锁定了一个副本,没什么意义。

完整代码:

[csharp]  view plain  copy
  1. namespace Invok  
  2. {  
  3.     class Program  
  4.     {  
  5.         static int account = 1000;//账户    
  6.         static int pocket = 0;//口袋   
  7.   
  8.         static void Main(string[] args)  
  9.         {  
  10.             int threadCount = 10;    
  11.             var threads = new Thread[threadCount];    
  12.             for (int i = 0; i < threadCount; i++)    
  13.             {    
  14.                 threads[i] = new Thread(DoWork);    
  15.                 threads[i].Start();    
  16.             }    
  17.             for (int i = 0; i < threadCount; i++)    
  18.             {    
  19.                 threads[i].Join();    
  20.             }    
  21.             Console.WriteLine("pocket=" + pocket);  
  22.             Console.ReadLine();  
  23.         }  
  24.   
  25.         static readonly object locker = new object();     
  26.         public static void DoWork()    
  27.         {  
  28.             lock (locker)  
  29.             {  
  30.                 if (account >= 1000)    
  31.                 {    
  32.                     Thread.Sleep(10);//自动取款机打了个小盹    
  33.                     account -= 1000;    
  34.                     pocket += 1000;    
  35.                 }   
  36.             }  
  37.         }       
  38.     }  
  39. }  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值