C#多线程开发:使用lock语句同步多个线程
2017-09-22 18:36
387 查看
在多个线程之间共享数据时,需要考虑线程同步问题,必须确保每次只有一个线程访问和改变共享数据。
C#中使用lock语句可以轻松地设置和解除锁定以期达到每次只有一个线程访问和改变共享数据的目的。
下面是一个多线程访问共享数据的实例,看看在没有进行同步操作的情况下会出现什么样的问题?
[csharp] view
plain copy
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace Invok
{
class Program
{
static int account = 1000;//账户
static int pocket = 0;//口袋
static void Main(string[] args)
{
int threadCount = 10;
var threads = new Thread[threadCount];
for (int i = 0; i < threadCount; i++)
{
threads[i] = new Thread(DoWork);
threads[i].Start();
}
for (int i = 0; i < threadCount; i++)
{
threads[i].Join();
}
Console.WriteLine("pocket=" + pocket);
Console.ReadLine();
}
public static void DoWork()
{
if (account >= 1000)
{
Thread.Sleep(10);//自动取款机打了个小盹
account -= 1000;
pocket += 1000;
}
}
}
}
可以将示例代码理解成:一个用户分十次从自己的银行账户中取钱。
取钱的逻辑由下面的代码来实现。
[csharp] view
plain copy
if (account >= 1000)
{
<span style="white-space:pre"> </span>Thread.Sleep(10);//自动取款机打了个小盹
<span style="white-space:pre"> </span>account -= 1000;
pocket += 1000;
}
当账户中的余额大于等于1000时,就取出1000放进自己的口袋。
因为用户当前的账户中仅剩下1000,所以就算用户取了10次,最终口袋中也应该只有1000。那么实际情况又是怎样的呢?
请看下面的执行结果(结果也可能是1000,2000,...,9000中的一个)。
结果竟然是10000!!!!!!!!
用户从仅有1000余额的账户中取出了10000,实在是一件令人振奋人心的事。
不过对于银行来说,这可不是件什么好事,因为照这样下去,银行的钱迟早会被用户掏空。
为什么会出现这样的结果呢?
这是因为没有对多线程访问共享数据进行同步,10个线程同时进入了取钱的逻辑,所以一共取出了10000。
为了解决这个问题,可以使用lock语句来同步多线程访问共享数据。下面是增加lock语句后的取钱逻辑。
[csharp] view
plain copy
static readonly object locker = new object();
public static void DoWork()
{
<span style="white-space:pre"> </span>lock (locker)
{
if (account >= 1000)
{
Thread.Sleep(10);//自动取款机打了个小盹
account -= 1000;
pocket += 1000;
}
}
}
代码中使用lock关键字锁定对象o,当一个线程获得锁定后,其他线程就无法再获得锁定,只有当当前线程解除锁定后,其他对象才可以重新获得锁定,这样一来,就可以保证每次只有一个线程获得锁定进而访问和修改共享数据。
多次执行修改后的示例代码,每次都可以得到以下的正确结果。
lock语句锁定的对象,必须是引用类型。因为锁定值类型只是锁定了一个副本,没什么意义。
完整代码:
[csharp] view
plain copy
namespace Invok
{
class Program
{
static int account = 1000;//账户
static int pocket = 0;//口袋
static void Main(string[] args)
{
int threadCount = 10;
var threads = new Thread[threadCount];
for (int i = 0; i < threadCount; i++)
{
threads[i] = new Thread(DoWork);
threads[i].Start();
}
for (int i = 0; i < threadCount; i++)
{
threads[i].Join();
}
Console.WriteLine("pocket=" + pocket);
Console.ReadLine();
}
static readonly object locker = new object();
public static void DoWork()
{
lock (locker)
{
if (account >= 1000)
{
Thread.Sleep(10);//自动取款机打了个小盹
account -= 1000;
pocket += 1000;
}
}
}
}
}
C#中使用lock语句可以轻松地设置和解除锁定以期达到每次只有一个线程访问和改变共享数据的目的。
下面是一个多线程访问共享数据的实例,看看在没有进行同步操作的情况下会出现什么样的问题?
[csharp] view
plain copy
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace Invok
{
class Program
{
static int account = 1000;//账户
static int pocket = 0;//口袋
static void Main(string[] args)
{
int threadCount = 10;
var threads = new Thread[threadCount];
for (int i = 0; i < threadCount; i++)
{
threads[i] = new Thread(DoWork);
threads[i].Start();
}
for (int i = 0; i < threadCount; i++)
{
threads[i].Join();
}
Console.WriteLine("pocket=" + pocket);
Console.ReadLine();
}
public static void DoWork()
{
if (account >= 1000)
{
Thread.Sleep(10);//自动取款机打了个小盹
account -= 1000;
pocket += 1000;
}
}
}
}
可以将示例代码理解成:一个用户分十次从自己的银行账户中取钱。
取钱的逻辑由下面的代码来实现。
[csharp] view
plain copy
if (account >= 1000)
{
<span style="white-space:pre"> </span>Thread.Sleep(10);//自动取款机打了个小盹
<span style="white-space:pre"> </span>account -= 1000;
pocket += 1000;
}
当账户中的余额大于等于1000时,就取出1000放进自己的口袋。
因为用户当前的账户中仅剩下1000,所以就算用户取了10次,最终口袋中也应该只有1000。那么实际情况又是怎样的呢?
请看下面的执行结果(结果也可能是1000,2000,...,9000中的一个)。
结果竟然是10000!!!!!!!!
用户从仅有1000余额的账户中取出了10000,实在是一件令人振奋人心的事。
不过对于银行来说,这可不是件什么好事,因为照这样下去,银行的钱迟早会被用户掏空。
为什么会出现这样的结果呢?
这是因为没有对多线程访问共享数据进行同步,10个线程同时进入了取钱的逻辑,所以一共取出了10000。
为了解决这个问题,可以使用lock语句来同步多线程访问共享数据。下面是增加lock语句后的取钱逻辑。
[csharp] view
plain copy
static readonly object locker = new object();
public static void DoWork()
{
<span style="white-space:pre"> </span>lock (locker)
{
if (account >= 1000)
{
Thread.Sleep(10);//自动取款机打了个小盹
account -= 1000;
pocket += 1000;
}
}
}
代码中使用lock关键字锁定对象o,当一个线程获得锁定后,其他线程就无法再获得锁定,只有当当前线程解除锁定后,其他对象才可以重新获得锁定,这样一来,就可以保证每次只有一个线程获得锁定进而访问和修改共享数据。
多次执行修改后的示例代码,每次都可以得到以下的正确结果。
lock语句锁定的对象,必须是引用类型。因为锁定值类型只是锁定了一个副本,没什么意义。
完整代码:
[csharp] view
plain copy
namespace Invok
{
class Program
{
static int account = 1000;//账户
static int pocket = 0;//口袋
static void Main(string[] args)
{
int threadCount = 10;
var threads = new Thread[threadCount];
for (int i = 0; i < threadCount; i++)
{
threads[i] = new Thread(DoWork);
threads[i].Start();
}
for (int i = 0; i < threadCount; i++)
{
threads[i].Join();
}
Console.WriteLine("pocket=" + pocket);
Console.ReadLine();
}
static readonly object locker = new object();
public static void DoWork()
{
lock (locker)
{
if (account >= 1000)
{
Thread.Sleep(10);//自动取款机打了个小盹
account -= 1000;
pocket += 1000;
}
}
}
}
}
相关文章推荐
- C#多线程开发6:使用lock语句同步多个线程
- C#多线程开发7:使用Monitor类同步多个线程
- 应用程序中的所有线程都可以访问方法中的公用字段。要同步对公用字段的访问,您可以使用属性替代字段,并使用 ReaderWriterLock 对象控制访问。为此,请按照下列步骤操作:
- iOS多线程开发小demo4,线程的同步问题
- python多线程编程: 使用互斥锁同步线程
- Linux多线程——使用信号量同步线程
- Java:多线程,线程同步,同步锁(Lock)的使用(ReentrantLock、ReentrantReadWriteLock)
- !!! 多线程编程+MFC中的多线程开发+线程间通讯+线程的同步
- Linux多线程——使用信号量同步线程
- Java:多线程,线程同步,同步锁(Lock)的使用(ReentrantLock、ReentrantReadWriteLock)
- 线程系列07,使用lock语句块或Interlocked类型方法保证自增变量的数据同步
- java多线程-使用ReadWriteLock同步数据访问
- 使用lock同步线程,建立了10个线程实例
- 黑马程序员-Java 多线程(二)-线程的同步、死锁、Lock接口
- 【iOS开发-多线程】使用NSOperation创建线程(使用较多)
- 多线程---使用ManualResetEvent来控制线程间的同步(实现了消费者和生产者模式)
- Java:多线程,使用同步锁(Lock)时利用Condition类实现线程间通信
- 嵌入式Linux应用程序开发——多线程3(线程的同步——条件变量)
- java开发线程篇1:java中有几种方法可以实现一个线程?用什么关键字修饰同步方法? stop()和suspend()方法为何不推荐使用?
- C# 多线程开发 1:使用 Thread 类创建与启动线程