编辑
2025-09-24
C#
00

目录

摘要
正文
举例说明
如何避免死锁

摘要

线程死锁通常发生在两个或更多的线程因为互相等待对方释放资源而无限期阻塞的情况。在C#中,死锁通常涉及到几种共享资源(例如锁、文件、数据库连接等)和线程之间的相互作用。造成线程死锁的原因主要包括以下几点:

  1. 相互排斥:每个资源要么已经分配给一个线程,要么就是可用的。如果一个资源已经分配给了一个线程,其他的线程就必须等待直到资源被释放。
  2. 占有和等待:一个线程至少已经持有一个资源,并且正在等待获取额外的资源,而这些额外的资源可能被其他线程占有。
  3. 非抢占:资源只能由占有它的线程自愿释放,不能被抢占。
  4. 循环等待:存在一种线程的等待循环,其中每个线程都在等待另一个线程所占有的资源。这形成了一个循环链,其中每个线程都在等待下一个线程释放资源。

正文

举例说明

假设有两个线程(线程A和线程B)以及两个资源(资源1和资源2)。线程A已经占有资源1,并且想要获取资源2。同时,线程B已经占有资源2,并且想要获取资源1。如果线程A和线程B都不释放它们各自已经占有的资源,那么它们都将无限期地等待对方,从而造成死锁。

如何避免死锁

  • 避免占有和等待:一种方法是确保线程在开始执行前一次性获取所有需要的资源。
  • 使用超时:在尝试获取资源时使用超时,这样线程在等待过长时间后可以放弃,释放已持有的资源,然后重试。
  • 资源排序:对资源进行排序,并强制每个线程按照相同的顺序请求资源,这可以避免循环等待的条件。
  • 使用锁的层级结构:通过定义锁的层级结构,并且规定线程可以按照从低到高的顺序获取锁,但不能反过来,以此来避免死锁。
C#
int x = 1; object lock1 = new object(); object lock2 = new object(); private void btnStart_Click(object sender, EventArgs e) { Thread t1 = new Thread(Read); t1.Start(); lock (lock2) { Thread.Sleep(2000); lock (lock1) { this.BeginInvoke(new Action(() => { txtWrite.Text = " Write"; })); } } } void Read() { lock (lock1) { Thread.Sleep(1000); lock (lock2) { x += 10; this.BeginInvoke(new Action(() => { txtRead.Text = "Read" + x.ToString(); })); } } }

image.png

点击开始后,出现死锁

直接使用Monitor类。其拥有TryEnter方法,该方法接受一个超时参数。如果在我们能够获取被lock保护的资源之前,超时参数过期,则该方法会返回 false.

C#
int x = 1; object lock1 = new object(); object lock2 = new object(); private void btnStart_Click(object sender, EventArgs e) { Thread t1 = new Thread(Read); t1.Start(); lock (lock2) { Thread.Sleep(1000); if (Monitor.TryEnter(lock1, 5000)) { lock (lock1) { this.BeginInvoke(new Action(() => { txtWrite.Text = " Write"; })); } } } } void Read() { lock (lock1) { Thread.Sleep(1000); lock (lock2) { x += 10; this.BeginInvoke(new Action(() => { txtRead.Text = "Read" + x.ToString(); })); } } }

本文作者:技术老小子

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!