一个简单的C#死锁程序

  Jeffrey Richter在《Windows核心编程(第5版)》中描述了一个死锁情况:假设线程Thread1和Thread2均需要独占方式访问互斥资源m_res1、m_res2,应用互斥对象Monitor在使用前Enter(相当于加锁)、使用后Exit(相当于解锁)。由于涉及到两个资源,此时需要特别注意加锁的顺序。如果两个线程的加锁顺序不同(Thread1先锁m_res2、Thread2先锁m_res1),此时容易发生死锁。依据该思路,下面给出了C#实现的完整程序代码:

class Program{ private static object m_res1 = new object(); private static object m_res2 = new object(); private static int m_count = 0; static void Main(string[] args) { Thread t1 = new Thread(Thread1); Thread t2 = new Thread(Thread2); t1.Start(); t2.Start(); while (true) { int preCount = m_count; Thread.Sleep(0); // 放弃当前线程的CPU时间片,Windows可能调度其他线程 if (preCount == m_count) // 数据没有变化,表明线程没有执行 { Console.WriteLine("dead lock! count: {0}", m_count); } } } private static void Thread1() { while (true) { Monitor.Enter(m_res2); // 先锁 m_res2 Monitor.Enter(m_res1); // 再锁 m_res1 m_count++; Monitor.Exit(m_res1); // 释放锁不存在先后关系 Monitor.Exit(m_res2); } } private static void Thread2() { while (true) { Monitor.Enter(m_res1); // 先锁 m_res1 Monitor.Enter(m_res2); m_count++; Monitor.Exit(m_res1); Monitor.Exit(m_res2); } }}
  运行上述程序时,一般计数到300左右(笔者的机器)就发生死锁现象了。其原因为:如果Thread1锁住m_res2同时、Thread1获得m_res1的锁,那么Thread1就不能获得m_res1的锁,当然Thread2也不能获得m_res2的锁。这两个线程都不能继续下去,于是m_count的值没有变化。

如果线程Thread1的加锁顺序与线程Thread2相同,即:

Monitor.Enter(m_res1);
Monitor.Enter(m_res2);

  此时,将不会出现死锁情况。假设Thread1锁住m_res1且申请锁m_res2,Thread2锁住m_res2并申请m_res1,可以证明不会出现这种情况:因为Thread2锁住m_res2时要么是先锁住了m_res1,要么是释放m_res1时来不及释放m_res2。第一种现象不可能,因为Thread1锁住了m_res1;第二种现象是释放锁,不存在马上申请锁。所以,顺序相同时不会出现死锁情况。

  解决的方法还有:使用bool Monitor.TryEnter()方法,尝试加锁并设定一个时间上界。如果锁不住,则放弃加锁,做其他操作。

  总结:在多线程对多个资源加锁时,一定要按相同的顺序。

文章来源:http://www.evget.com/zh-CN/Info/ReadInfo.aspx?id=9084
<think>嗯,用户让我举一个C#死锁程序例子。首先,我需要确认自己对死锁的理解是否正确。死锁通常发生在多线程环境中,当两个或多个线程互相等待对方释放资源时,就会陷入无限等待的状态。这时候程序就会卡住,无法继续执行。 接下来,我需要考虑如何在C#中构造这样的场景。C#里常用的多线程机制有Thread类、Task,还有lock关键字。lock关键字用于确保代码块在同一时间只能被一个线程访问,这可能会导致死锁,如果两个线程分别以不同的顺序获取多个锁的话。 比如,线程1先获取锁A,然后尝试获取锁B;而线程2先获取锁B,再尝试获取锁A。如果这两个线程同时运行,就可能出现线程1持有锁A等待锁B,线程2持有锁B等待锁A的情况,形成死锁。 我需要确保这个例子足够简单,让用户能够理解。可能需要两个对象作为锁,两个线程分别以不同的顺序请求这两个锁。然后展示如何运行这两个线程,并观察程序是否卡住。 另外,用户可能希望看到实际的代码示例,所以需要写出完整的C#代码,包括using语句、类定义、Main方法等。同时,注释解释关键部分,比如锁的顺序和线程的启动。 还要注意,在C#中使用lock时,通常建议使用私有对象作为锁,而不是公共对象,以避免外部代码锁定同一对象导致意外行为。但在这个例子中,为了演示死锁,可能需要显式地使用两个不同的对象作为锁。 可能还需要考虑线程的启动方式,比如使用Thread.Start(),然后调用Join()等待线程完成,但实际情况下,由于死锁的存在,Join()会一直等待,程序无法结束。用户可能需要手动终止程序。 最后,需要验证这个例子是否真的会导致死锁。编写代码后,运行测试,确认程序确实卡住,无法继续执行。如果有必要,可以添加一些控制台输出,显示线程的执行步骤,帮助用户理解死锁发生的过程。 总结一下,步骤是: 1. 定义两个锁对象。 2. 创建两个线程,每个线程以不同的顺序请求这两个锁。 3. 在每个线程中,先获取第一个锁,然后休眠一段时间,再获取第二个锁。 4. 启动线程并等待,观察是否发生死锁。 确保代码简洁,注释清晰,用户能够轻松复制并运行测试。同时,在回答末尾添加相关问题,帮助用户进一步理解死锁的预防和解决方法。</think>以下是一个简单C#死锁程序示例,通过两个线程以不同顺序请求锁资源导致死锁: ```csharp using System; using System.Threading; class DeadlockExample { static object lock1 = new object(); static object lock2 = new object(); static void Main() { Thread threadA = new Thread(MethodA); Thread threadB = new Thread(MethodB); threadA.Start(); threadB.Start(); threadA.Join(); // 程序会在此处永久卡住 threadB.Join(); } static void MethodA() { lock (lock1) { Console.WriteLine("线程A获得lock1"); Thread.Sleep(100); // 确保线程B有时间获取lock2 lock (lock2) // 此处等待线程B释放lock2 { Console.WriteLine("线程A获得lock2"); } } } static void MethodB() { lock (lock2) { Console.WriteLine("线程B获得lock2"); Thread.Sleep(100); // 确保线程A有时间获取lock1 lock (lock1) // 此处等待线程A释放lock1 { Console.WriteLine("线程B获得lock1"); } } } } ``` **死锁过程说明**: 1. 线程A获取`lock1`后休眠,线程B获取`lock2`后休眠 2. 线程A尝试获取`lock2`(被线程B持有) 3. 线程B尝试获取`lock1`(被线程A持有) 4. 双方无限等待对方释放资源
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值