C# 中的Monitor和thread.join

本文深入探讨了C#中Monitor类的使用方法及其与lock关键字的关系,解析了线程状态转换机制,包括runing、wait和ready状态,并通过代码示例展示了Monitor.Wait()和Monitor.Pulse()如何实现线程间的资源传递,避免死锁。

Lock内部调用的就是Monitor
其实lock在IL代码中会被翻译成Monitor。
也就是Monitor.Enter(obj)和Monitor.Exit(obj).
lock(obj)
{
}
等价为:
try
{

Monitor.Enter(obj)
}
catch()
{}
finally
{

onitor.Exit(obj)  

}
主要说Monitor的两个方法,wait()和pause()
在lock代码里面如果调用了Monitor.Wait(),会放弃对资源的所有权,让别的线程lock进来。然后别的线程代码里Pulse一下(让原线程进入到等待队列),然后在Wait一下释放资源,这样原线程的就可以继续执行了(代码还堵塞在wait那句话呢)。
也就是说,必须两个或多个线程共同调用Wait和Pulse,把资源的所有权抛来抛去,才不会死锁。

线程主要有三种状态
①runing
②wait
③ready

  1. pulse(obj)用于将线程从wait状态跟改为ready状态,当此线程执行完成时,或者此线程状态更改为wait时,将从ready状态中选择执行
  2. wait(obj),将线程从runing状态更改为wati状态,若要继续执行,需要先更改为ready状态
代码示例:
static void Main(string[] args)
        {
            object obj = new object();
            Task.Run(() => {
                lock (obj)
                {
                    Console.WriteLine("lockA");
                    Thread.Sleep(1000);
                    //此线程状态更改为wait,进入阻塞状态
                    Monitor.Wait(obj);
                    Monitor.Pulse(obj);
                }
                Console.WriteLine("Aover");
            });
            Task.Run(() => {
                lock (obj)
                {
                    Console.WriteLine("lockB");
                    Thread.Sleep(500);
                    Monitor.Pulse(obj);
                    Monitor.Wait(obj);
                }
                Console.WriteLine("Bover");
            });

结果:
lockA
lockB
Aover
Bover
两个线程间踢皮球
A->B->A-B

Thread.Join()

Thread.Join()相当于task.wait(); 阻塞当前线程,等待任务完成。

和AutoEvent相似是处理同步关系的,但是AutoEvent是跨进程的,而Monitor是针对线程的。

<think>嗯,用户这次问的是C# 4.0中Monitor.TryEnter的具体用法注意事项。从用户提供的引用内容来看,ta应该是个.NET开发者,正在处理多线程同步问题。引用[3]里提到了UI线程后台线程的交互,引用[4]提到了Thread.JoinInterLocked,说明用户对基础线程同步有了解,现在需要更精细的锁控制。 用户特意强调要C# 4.0版本,这个细节很重要。Monitor.TryEnter在4.0之前就有,但4.0的TPL对多线程编程有重大改进。虽然用户没提Task,但引用[1]提到Task比Thread性能更好,可能在后续优化时会有用。 关于Monitor.TryEnter的核心要点:1)它lock的区别在于非阻塞特性;2)超时机制是重点;3)资源释放的可靠性。用户引用[4]里提到InterLocked,这个对比点必须包含,因为轻量级操作时InterLocked效率更高。 特别注意用户引用[3]的UI跨线程访问案例。虽然Monitor本身不涉及UI,但如果在UI线程执行TryEnter导致阻塞,会引发界面卡顿。这个实际影响要写在注意事项里。 需要强调finally块的必须性。看到引用[3]中正确使用了InvokeRequired模式,说明用户有代码健壮性意识,这点可以呼应——锁管理同样需要这样严谨。 最后版本适配要说明清楚:TryEnter在4.0没有行为变化,但整个线程模型因Task而不同。用户如果从Thread迁移到Task,可能会遇到MonitorTask的兼容问题,这个冷知识值得补充。 </think>以下是关于C# 4.0中`Monitor.TryEnter`方法的详细指南,结合关键注意事项最佳实践: --- ### **一、Monitor.TryEnter的核心作用** 提供**非阻塞**的锁获取机制,尝试获取指定对象的独占锁: ```csharp if (Monitor.TryEnter(lockObject)) { try { // 临界区代码 } finally { Monitor.Exit(lockObject); // 必须释放锁 } } else { // 锁获取失败时的备用逻辑 } ``` --- ### **二、关键重载方法** C# 4.0支持三个重要重载: ```csharp // 1. 立即尝试获取锁 bool TryEnter(object obj) // 2. 指定超时时间(毫秒) bool TryEnter(object obj, int millisecondsTimeout) // 3. 通过ref bool参数返回锁状态 bool TryEnter(object obj, ref bool lockTaken) ``` --- ### **三、必须遵循的注意事项** 1. **可靠锁释放** 必须使用`try-finally`确保锁释放: ```csharp bool lockTaken = false; try { Monitor.TryEnter(lockObject, ref lockTaken); if (lockTaken) { /* 操作共享资源 */ } } finally { if (lockTaken) Monitor.Exit(lockObject); // 避免死锁[^3] } ``` 2. **避免跨线程释放锁** 获取锁释放锁必须在**同一线程**执行,否则抛出`SynchronizationLockException`。 3. **锁对象选择原则** 使用`private readonly object`实例(而非值类型或`this`): ```csharp private readonly object _locker = new object(); // 推荐[^3] ``` 4. **超时机制的重要性** 指定超时可防止线程无限阻塞: ```csharp if (Monitor.TryEnter(_locker, 500)) // 等待500ms { // ... } ``` --- ### **四、lock关键字的对比** | **特性** | `lock`关键字 | `Monitor.TryEnter` | |------------------|----------------------|--------------------------| | 阻塞行为 | 阻塞直到获取锁 | 可设置非阻塞或超时等待 | | 异常安全性 | 自动释放锁 | 需手动确保释放 | | 适用场景 | 简单同步 | 复杂超时/死锁预防逻辑 | --- ### **五、典型应用场景** 1. **死锁预防** 尝试获取多个锁时设定超时: ```csharp if (Monitor.TryEnter(lockA, 100)) { try { if (Monitor.TryEnter(lockB, 100)) { // 成功获取双锁 } } finally { /* 释放lockB */ } } finally { /* 释放lockA */ } ``` 2. **资源争用时的优雅降级** 当无法立即获取锁时执行替代操作: ```csharp if (!Monitor.TryEnter(cacheLock)) { return GetFromBackupStorage(); // 锁忙时使用备用方案 } ``` --- ### **六、陷阱规避指南** 1. **避免锁住公共对象** ❌ 错误:`Monitor.Enter(this);` ✅ 正确:使用私有对象`_syncRoot` 2. **值类型装箱问题** 禁止对值类型加锁(每次装箱产生新对象): ```csharp int number = 10; Monitor.Enter((object)number); // 危险!每次装箱不同对象 ``` 3. **递归锁限制** 同一线程可重复获取锁,但必须**匹配释放次数**: ```csharp Monitor.Enter(_lock); // 计数+1 Monitor.Enter(_lock); // 计数+2 Monitor.Exit(_lock); // 计数-1 Monitor.Exit(_lock); // 计数归零 ``` --- ### **七、性能优化建议** - 对于**高频短临界区**,考虑`Interlocked`或`SpinLock`替代方案[^4] - 使用`.NET 4.0的Parallel Tasks Library`处理计算密集型任务,减少手动锁需求[^1] ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值