接下来就为大家详细讲解C#多线程编程中的锁系统基本用法的完整攻略。
什么是锁(Lock)
锁用于多线程编程中,是一种用于避免竞争访问共享资源的同步机制。在多线程程序中,多个线程可能同时访问同一个共享资源,如果不采取任何措施,就会产生“竞态条件”,导致程序出现不可预期问题。
为了避免这种情况,我们可以引入锁机制,来确保只有一个线程能够同时访问共享资源,从而避免问题的出现。
.NET Framework提供了一些锁的实现,比如Monitor、Mutex和Semaphore等,本文就以Monitor为例,介绍C#多线程编程中的锁系统的基本用法。
Monitor的基本用法
Monitor是C#中最常用的锁实现之一,可以用于确保多个线程访问同一个共享资源时的同步。
Monitor的主要用法有以下2个:
1.获取对象锁
调用Monitor.Enter方法可以获取某个对象的独占锁,该方法会阻塞当前线程,直到获取该对象的独占锁,代码示例:
class Program {
static readonly object obj = new object();
static void Main(string[] args) {
Console.WriteLine("Main Thread starting...");
Thread thread = new Thread(DoSomeWork);
thread.Start();
lock (obj) {
Console.WriteLine("Thread A Acquires the lock...");
Console.WriteLine("Thread A is executing some code...");
Thread.Sleep(5000);
Console.WriteLine("Thread A Releasing the lock...");
}
Console.WriteLine("Main Thread Exiting...");
}
static void DoSomeWork() {
Console.WriteLine("Thread B Trying to acquire lock...");
lock (obj) {
Console.WriteLine("Thread B Acquires the lock...");
Console.WriteLine("Thread B is executing some code...");
Thread.Sleep(5000);
Console.WriteLine("Thread B Releasing the lock...");
}
}
}
在上面的示例代码中,我们使用了lock语句获取了obj对象的锁,从而防止了线程A和线程B同时访问obj对象的问题。
注意,在线程A访问代码块时,线程B是无法访问该代码块的,因为lock语句获取的是对象锁,而不是线程锁,只要其他线程试图获取相同的对象锁,就会被阻塞。
2.释放对象锁
通过Monitor.Exit方法可以释放Monitor.Enter所获取的对象锁,代码示例:
class Program {
static readonly object obj = new object();
static void Main(string[] args) {
Console.WriteLine("Main Thread starting...");
Thread thread = new Thread(DoSomeWork);
thread.Start();
lock (obj) {
Console.WriteLine("Thread A Acquires the lock...");
Console.WriteLine("Thread A is executing some code...");
Thread.Sleep(5000);
Console.WriteLine("Thread A Releasing the lock...");
Monitor.Pulse(obj);
}
Console.WriteLine("Main Thread Exiting...");
}
static void DoSomeWork() {
lock (obj) {
Console.WriteLine("Thread B Trying to acquire lock...");
Monitor.Wait(obj);
Console.WriteLine("Thread B Acquires the lock...");
Console.WriteLine("Thread B is executing some code...");
Thread.Sleep(5000);
Console.WriteLine("Thread B Releasing the lock...");
}
}
}
在上面的代码中,我们在获取对象锁之后,调用了Monitor.Pulse方法通知等待该锁的线程(这里是线程B)可以开始执行了。
另外,我们在线程B中使用了Monitor.Wait(obj)方法,将该线程放到等待队列中,等到Monitor.Pulse(obj)被调用时,才会重新尝试获取obj对象的锁。
示范
接下来我们结合一个生产者消费者的实例来看看Monitor的用法。
/// <summary>
/// 产品的模型类
/// </summary>
public class Product {
/// <summary>
/// 产品的名称
/// </summary>
public string ProductName { get; set; }
}
class Program {
static readonly object lockObj = new object();
static Queue<Product> products = new Queue<Product>();
static void Main(string[] args) {
new Thread(Producer).Start();
new Thread(Consumer).Start();
Console.ReadLine();
}
/// <summary>
/// 生产者方法
/// </summary>
static void Producer() {
while (true) {
lock (lockObj) {
while (products.Count >= 10) {
Monitor.Wait(lockObj); //队列已满,等待消费者消费产品
}
Product product = new Product {
ProductName = Guid.NewGuid().ToString()
};
products.Enqueue(product);
Console.WriteLine($"添加一个产品,目前产品数量:{products.Count}");
if (products.Count == 1) {
Monitor.Pulse(lockObj); //通知消费者有产品可以消费
}
}
}
}
/// <summary>
/// 消费者方法
/// </summary>
static void Consumer() {
while (true) {
lock (lockObj) {
while (products.Count == 0) {
Monitor.Wait(lockObj); //队列已空,等待生产者生产产品
}
Product product = products.Dequeue();
Console.WriteLine($"消费一个产品,目前产品数量:{products.Count}");
if (products.Count == 9) {
Monitor.Pulse(lockObj); //通知生产者可以继续生产产品
}
}
}
}
}
上述代码中,由于产品队列为共享资源,producer和consumer都需要操作该队列,因此需要使用lock进行同步,以避免竞争访问。
同时,producer生产产品时,如果队列已满,需要等待消费者消费产品后再继续生产(Monitor.Wait);consumer消费产品时,如果队列为空,需要等待生产者生产产品后再消费(Monitor.Wait)。
在生产者生产产品后,会通过Monitor.Pulse方法通知消费者可以消费产品了,在消费者消费产品后,会通过Monitor.Pulse方法通知生产者可以继续生产产品了。这样,就实现了生产者与消费者之间的同步。
到这里,我们就介绍了C#多线程编程中锁的基本用法,主要包括获取对象锁和释放对象锁。同时结合生产者消费者的实例,为大家演示了如何使用Monitor实现线程同步。
希望大家能够掌握锁的基本用法,并且能够合理运用在自己的多线程程序中。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C#多线程编程中的锁系统基本用法 - Python技术站