本篇从Monitor,Mutex,ManualResetEvent,AutoResetEvent,WaitHandler的类关系图开始,希望通过本篇的介绍能对常见的线程同步方法有一个整体的认识,而对每种方式的使用细节,适用场合不会过多解释。让我们来看看这几个类的关系图:
1.lock关键字
lock是C#关键词,它将语句块标记为临界区,确保当一个线程位于代码的临界区时,另一个线程不进入临界区。如果其他线程试图进入锁定的代码,则它将一直等待(即被阻止),直到该对象被释放。方法是获取给定对象的互斥锁,执行语句,然后释放该锁。
MSDN上给出了使用lock时的注意事项通常,应避免锁定 public 类型,否则实例将超出代码的控制范围。常见的结构 lock (this)、lock (typeof (MyType)) 和 lock ("myLock") 违反此准则。
1)如果实例可以被公共访问,将出现 lock (this) 问题。
2)如果 MyType 可以被公共访问,将出现 lock (typeof (MyType)) 问题由于一个类的所有实例都只有一个类型对象(该对象是typeof的返回结果),锁定它,就锁定了该对象的所有实例。微软现在建议不要使用 lock(typeof(MyType)),因为锁定类型对象是个很缓慢的过程,并且类中的其他线程、甚至在同一个应用程序域中运行的其他程序都可以访问该类型对象,因此,它们就有可能代替您锁定类型对象,完全阻止您的执行,从而导致你自己的代码的挂起。
3)由于进程中使用同一字符串的任何其他代码将共享同一个锁,所以出现 lock(“myLock”) 问题。这个问题和.NET Framework创建字符串的机制有关系,如果两个string变量值都是"myLock",在内存中会指向同一字符串对象。
最佳做法是定义 private 对象来锁定, 或 private static对象变量来保护所有实例所共有的数据。
我们再来看看lock关键字的本质,lock关键字其实就是对Monitor类的Enter()和Exit()方法的封装,并通过try...catch...finally语句块确保在lock语句块结束后执行Monitor.Exit()方法,释放互斥锁。
2.Monitor类
Monitor类通过向单个线程授予对象锁来控制对对象的访问。对象锁提供限制访问临界区的能力。当一个线程拥有对象的锁时,其他任何线程都不能获取该锁。还可以使用 Monitor 来确保不会允许其他任何线程访问正在由锁的所有者执行的应用程序代码节,除非另一个线程正在使用其他的锁定对象执行该代码。
通过对lock关键字的分析我们知道,lock就是对Monitor的Enter和Exit的一个封装,而且使用起来更简洁,因此Monitor类的Enter()和Exit()方法的组合使用可以用lock关键字替代。
另外Monitor类还有几个常用的方法:
TryEnter()能够有效的解决长期死等的问题,如果在一个并发经常发生,而且持续时间长的环境中使用TryEnter,可以有效防止死锁或者长时间的等待。比如我们可以设置一个等待时间bool gotLock = Monitor.TryEnter(myobject,1000),让当前线程在等待1000秒后根据返回的bool值来决定是否继续下面的操作。
Wait()释放对象上的锁以便允许其他线程锁定和访问该对象。在其他线程访问对象时,调用线程将等待。脉冲信号用于通知等待线程有关对象状态的更改。
Pulse(),PulseAll()向一个或多个等待线程发送信号。该信号通知等待线程锁定对象的状态已更改,并且锁的所有者准备释放该锁。等待线程被放置在对象的就绪队列中以便它可以最后接收对象锁。一旦线程拥有了锁,它就可以检查对象的新状态以查看是否达到所需状态。
注意:Pulse、PulseAll和Wait方法必须从同步的代码块内调用。
我们假定一种情景:妈妈做蛋糕,小孩有点馋,妈妈每做好一块就要吃掉,妈妈做好一块后,告诉小孩蛋糕已经做好了。下面的例子用Monitor类的Wait和Pulse方法模拟小孩吃蛋糕的情景。
//仅仅是说明Wait和Pulse/PulseAll的例子
//逻辑上并不严密,使用场景也并不一定合适
class MonitorSample
{
private int n = 1; //生产者和消费者共同处理的数据
private int max = 10000;
private object monitor = new object();
public void Produce()
{
lock (monitor)
{
for (; n <= max; n++)
{
Console.WriteLine("妈妈:第" + n.ToString() + "块蛋糕做好了");
//Pulse方法不用调用是因为另一个线程中用的是Wait(object,int)方法
//该方法使被阻止线程进入了同步对象的就绪队列
//是否需要脉冲激活是Wait方法一个参数和两个参数的重要区别
//Monitor.Pulse(monitor);
//调用Wait方法释放对象上的锁并阻止该线程(线程状态为WaitSleepJoin)
//该线程进入到同步对象的等待队列,直到其它线程调用Pulse使该线程进入到就绪队列中
//线程进入到就绪队列中才有条件争夺同步对象的所有权
//如果没有其它线程调用Pulse/PulseAll方法,该线程不可能被执行
Monitor.Wait(monitor);
}
}
}
public void Consume()
{
lock (monitor)
{
while (true)
{
//通知等待队列中的线程锁定对象状态的更改,但不会释放锁
//接收到Pulse脉冲后,线程从同步对象的等待队列移动到就绪队列中
//注意:最终能获得锁的线程并不一定是得到Pulse脉冲的线程
Monitor.Pulse(monitor);
//释放对象上的锁并阻止当前线程,直到它重新获取该锁
//如果指定的超时间隔已过,则线程进入就绪队列
Monitor.Wait(monitor,1000);
Console.WriteLine("孩子:开始吃第" + n.ToString() + "块蛋糕");
}
}
}
static void Main(string[] args)
{
MonitorSample obj = new MonitorSample();
Thread tProduce = new Thread(new ThreadStart(obj.Produce));
Thread tConsume = new Thread(new ThreadStart(obj.Consume));
//Start threads.
tProduce.Start();
tConsume.Start();
Console.ReadLine();
}
}
这个例子的目的是要理解Wait和Pulse如何保证线程同步的,同时要注意Wait(obeject)和Wait(object,int)方法的区别,理解它们的区别很关键的一点是要理解同步的对象包含若干引用,其中包括对当前拥有锁的线程的引用、对就绪队列(包含准备获取锁的线程)的引用和对等待队列(包含等待对象状态更改通知的线程)的引用。
- 大小: 29.5 KB
分享到:
相关推荐
多线程:C#线程同步lock,Monitor,Mutex,同步事件和等待句柄[整理].pdf
C#提供了多种机制来实现线程同步,包括lock关键字、Monitor、同步事件和等待句柄以及Mutex类。这些机制的主要目标是避免竞态条件,保证并发执行的线程能够正确地访问和修改共享数据。 首先,我们来看lock关键字。...
C#线程同步是多线程编程中的一个重要概念,它涉及到如何控制多个线程对共享资源的访问,以避免数据不一致性和竞态条件。在C#中,线程同步通常用于确保在某一时刻只有一个线程可以访问特定的代码块或资源,从而保证...
本文主要关注C#中的线程同步机制,特别是lock、Monitor、Mutex以及几种同步事件和等待句柄的使用。线程同步是为了避免多个线程同时访问共享资源,导致数据不一致或引发竞态条件等问题。 1. lock关键字: lock是C#中...
C#提供了多种同步机制,如Mutex、Semaphore、Monitor和lock关键字。生产者和消费者问题是线程同步的经典例子,生产者负责生成数据,消费者负责消费数据。通过使用Monitor或Mutex等同步工具,可以确保生产者不会在...
在C#中,线程同步可以通过多种方式实现,包括锁、线程安全的类、中断和终止、线程状态管理和等待句柄。 1. **线程同步基础** - **简易阻止方法**:如`Thread.Sleep`用于让线程暂停指定时间,`Thread.Join`用于等待...
C#提供了多种同步机制,如`Monitor`(基于监视器的同步)、`Mutex`(互斥锁)、`Semaphore`(信号量)和`EventWaitHandle`(事件等待句柄),以及锁定语句`lock`,用于控制对共享资源的访问。 4. **线程间通信**:`...
4. **线程同步的其他机制**:除了Mutex和Monitor外,C#还提供了其他同步机制,如Semaphore(信号量)用于控制同时访问特定资源的线程数量,和EventWaitHandle(事件等待句柄)用于线程间的通信和同步。 5. **死锁和...
通过对C#提供的同步机制的熟练运用,我们可以创建出高效且可靠的线程安全包装器,从而在多线程环境中安全地使用各种对象和数据结构。在实际项目中,结合具体需求选择合适的同步策略和线程安全包装器,是确保系统稳定...
### C#多线程教程 #### 一、多线程的相关概念 - **什么是进程?** - 进程是操作系统资源分配的基本单位,当一个程序启动时,它就会成为一个进程。一个进程不仅包含了程序本身,还包括了该程序运行所需的内存空间...
- **EventWaitHandle**:事件等待句柄可用于线程间的通信,通过`Set`和`Reset`方法通知线程开始或停止工作。 - **Barrier**:用于多线程间的同步,当所有线程到达屏障点时,它们将一起继续执行。 4. **线程局部...
4. **EventWaitHandle**:事件等待句柄提供了一种线程间通信的方式,通过设置和重置事件状态,线程可以通知其他线程继续执行或者等待。C#中的ManualResetEvent和AutoResetEvent就是其具体实现。 5. **Lock**:C#中...
C#提供了多种同步机制,如`Mutex`、`Semaphore`、`Monitor`(锁)以及`lock`关键字。例如,使用`lock`关键字可以确保在同一时刻只有一个线程能够访问特定对象: ```csharp lock (myObject) { // 临界区代码,只允许...
例如,`Thread`类用于创建和管理线程,而`Monitor`、`Mutex`、`Semaphore`、`EventWaitHandle`等类则提供了线程同步和互斥访问的手段。 1. **线程同步**:线程同步是指在多线程环境下,控制多个线程按照特定的顺序...
在IT领域,尤其是在软件开发中,"C# 系统挂机锁 实例源码(系统操作)"是一个关于进程管理、线程同步和资源保护的主题。C#是一种广泛使用的面向对象的编程语言,尤其适合构建Windows桌面应用程序和服务器端应用。在...
C#线程同步是多线程编程中的一个重要概念,它涉及到如何在多个线程同时运行时,有效地管理和协调对共享资源的访问,以避免数据不一致性和竞态条件。线程同步是通过控制线程执行的顺序和访问共享资源的方式来实现的,...