目录

async/await 异步编程死锁问题

目录

在异步编程中,如果稍有不注意,就会造成死锁问题。所谓死锁:即两个以上的线程同时争夺被互相锁住的资源,两个都不放手。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
using System;
using System.Threading.Tasks;

namespace async_await_deadlock
{
    class Program
    {
        static void Main(string[] args)
        {
            DeadLock();
        }

        static async Task WaitAsync()
        {
            await Task.Delay(TimeSpan.FromSeconds(10));
        }

        static void DeadLock()
        {
            var task = WaitAsync();
            task.Wait();
            Console.WriteLine("Hello World!");
        }
    }
}

以上这断代码会引发死锁问题, Hello world 不会被输出。
为何会造成死锁?首先, task.Wait() 方法造成了当前主线程被阻塞,其次, WaitAsync 方法中的 Task.Delay (由挂起到激活状态) 运行结束时会捕获当前的上下文线程,而当前的上下文线程已经被阻塞,故造成死锁。

  • 解决方案有以下两种方案
  1.  全部采用异步方式,修改后如下:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
using System;
using System.Threading.Tasks;

namespace async_await_deadlock
{
    class Program
    {
        static async Task Main(string[] args)
        {
            await DeadLock();
        }

        static async Task WaitAsync()
        {
            await Task.Delay(TimeSpan.FromSeconds(10));
        }

        static async Task DeadLock()
        {
            await WaitAsync();
            Console.WriteLine("Hello World!");
        }
    }
}
  1.  在异步方法中使用 Task.ConfigureAwait(false) ,不将 await 产生的延时任务送回到原始上下文线程中,即不会去捕获原始上下文,并在其新开辟的线程中结束其代码的运行,如下面的代码。

如果是这种方式,则在异步方法的 Task.ConfigureAwait(false) 语句之后的代码块中,获取不了原始上下文的某些变量,容易造成 NullReferenceException 空值引用异常。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
using System;
using System.Threading.Tasks;

namespace async_await_deadlock
{
    class Program
    {
        static void Main(string[] args)
        {
            DeadLock();
        }

        static async Task WaitAsync()
        {
            await Task.Delay(TimeSpan.FromSeconds(10)).ConfigureAwait(false);
        }

        static void DeadLock()
        {
            var task = WaitAsync();
            task.Wait();
            Console.WriteLine("Hello World!");
        }
    }
}