Task中Wait()和Result造成死锁

news/2024/11/7 18:28:55/

        在使用Task的时候,一不留神就会造成死锁,而且难以发现,尤其是业务繁多的情况下,一个Task嵌套另一个Task的时候,下面就演示一下,在什么情况下,会产生Wait()和Result的死锁,因此,我们就要避免这样的写法。

目录

一、Wait()死锁

二、Result死锁

一、Wait()死锁

首先执行下面这段代码,点击按钮的时候,界面直接就卡死了。

        private void Button_Click(object sender, RoutedEventArgs e){Console.Out.WriteLine(Thread.CurrentThread.ManagedThreadId);       //获取当前的线程IDA().Wait();Console.Out.WriteLine(Thread.CurrentThread.ManagedThreadId);}private async Task A(){await Task.Delay(1000);Console.Out.WriteLine(Thread.CurrentThread.ManagedThreadId);//业务代码}

如下图所示,而且运行显示的线程是1,也就是在执行A().Wait();时,程序就死了。

死去的原因就是A方法里面,要等待1s,它们都是主线程,所以到了 A().Wait()时,主线程会卡死这里,形成了互相等待的局面,你等我,我等你,就产生了死锁。

解决死锁的方式有2种。

1.只增加一句代码即可

增加.ConfigureAwait(false)

    private void Button_Click(object sender, RoutedEventArgs e){Console.Out.WriteLine(Thread.CurrentThread.ManagedThreadId);       //获取当前的线程IDA().Wait();Console.Out.WriteLine(Thread.CurrentThread.ManagedThreadId);}private async Task A(){await Task.Delay(1000).ConfigureAwait(false);Console.Out.WriteLine(Thread.CurrentThread.ManagedThreadId);//业务代码}

这句的意思就是,让你重新建立一个线程,把主线程让出去,这样就不会死锁了。

此时点击按钮,就会产生一个线程4,等线程4执行完毕后,就回到了主线程上。 

2. 增加await(推荐)

      private async void Button_Click(object sender, RoutedEventArgs e){Console.Out.WriteLine(Thread.CurrentThread.ManagedThreadId);       //获取当前的线程ID//A().Wait();                                                            //A().Wait();await A();Console.Out.WriteLine(Thread.CurrentThread.ManagedThreadId);}private async Task A(){await Task.Delay(1000);Console.Out.WriteLine(Thread.CurrentThread.ManagedThreadId);//业务代码}

此时点击按钮,会看到都是同样的线程

虽然都解决了死锁,但是他们的原理是不一样的,第2种,始终都是1个主线程再执行,第1个开启了一个线程,干完事后,又回到了主线程上。

微软也建议我们async到底,一直传染下去。 

二、Result死锁

这种死锁主要是Task中,带有返回的值。

我们改造一下即可

     private void Button_Click(object sender, RoutedEventArgs e){Console.Out.WriteLine(Thread.CurrentThread.ManagedThreadId);       //获取当前的线程IDstring str = A().Result;Console.Out.WriteLine(Thread.CurrentThread.ManagedThreadId);}private async Task<string> A(){await Task.Delay(1000);Console.Out.WriteLine(Thread.CurrentThread.ManagedThreadId);//业务代码return "123";}

此时点击按钮,界面卡死了

解决方式和上面的一样,同样有2种方式

1.增加.ConfigureAwait(false)

    private void Button_Click(object sender, RoutedEventArgs e){Console.Out.WriteLine(Thread.CurrentThread.ManagedThreadId);       //获取当前的线程IDstring str = A().Result;Console.Out.WriteLine(Thread.CurrentThread.ManagedThreadId);}private async Task<string> A(){await Task.Delay(1000).ConfigureAwait(false);Console.Out.WriteLine(Thread.CurrentThread.ManagedThreadId);//业务代码return "123";}

点击按钮后,界面就不会卡了,也是创建了一个线程,完成后,回到主线程上面 

2.增加await(推荐)

     private async void Button_Click(object sender, RoutedEventArgs e){Console.Out.WriteLine(Thread.CurrentThread.ManagedThreadId);       //获取当前的线程IDstring str =await A();Console.Out.WriteLine(Thread.CurrentThread.ManagedThreadId);}private async Task<string> A(){await Task.Delay(1000);Console.Out.WriteLine(Thread.CurrentThread.ManagedThreadId);//业务代码return "123";}

此时点击按钮,界面不卡了,会看到都是同样的线程,和上面的一模一样。

拓展

当我们基于第二部分的第2种方法,加上了.ConfigureAwait(false)

将会有什么变化呢?

代码:

    private async void Button_Click(object sender, RoutedEventArgs e){Console.Out.WriteLine(Thread.CurrentThread.ManagedThreadId);       //获取当前的线程IDstring str =await A();Console.Out.WriteLine(Thread.CurrentThread.ManagedThreadId);}private async Task<string> A(){await Task.Delay(1000).ConfigureAwait(false);Console.Out.WriteLine(Thread.CurrentThread.ManagedThreadId);//业务代码return "123";}

效果 

界面也不卡了,但是发现到,界面还是开启了一个线程,然后回到主线程上,虽然他们的功能都是一样的,但是这种方法肯定不如单个主线程好,因为开启一个线程,也需要耗费资源。 

所以,ConfigureAwait(false)这句代码非常的重要,界面是否卡死,就是他的原因,意思就是是否立即返回主线程干活,true是,false否。

当我们改成true,又是单个主线程执行了,此时,其实ConfigureAwait(true)是句无效的代码,因为就算你返回了,那边还有一句await,await主线程,都是在一个线程上。

来源:

Task中Wait()和Result造成死锁-CSDN博客


http://www.ppmy.cn/news/1255143.html

相关文章

Chat-GPT原理

GPT原理 核心是基于Transformer 架构 英文原文&#xff1a; ​ Transformers are based on the “attention mechanism,” which allows the model to pay more attention to some inputs than others, regardless of where they show up in the input sequence. For exampl…

掌握视频剪辑技巧,轻松自定义视频速率,打造个性化出彩视频

你是否曾经因为视频节奏平淡而缺乏吸引力而苦恼&#xff1f;现在&#xff0c;我们为你推荐一款视频批量剪辑工具&#xff0c;让你轻松自定义视频速率&#xff0c;实现出彩个性化视频。 首先第一步&#xff0c;我们要打开好简单批量智剪&#xff0c;并登录账号。 第二步&#x…

外包干了2年,技术退步明显。。。

前言 简单的说下&#xff0c;我大学的一个同学&#xff0c;毕业后我自己去了自研的公司&#xff0c;他去了外包&#xff0c;快两年了我薪资、技术各个方面都有了很大的提升&#xff0c;他在外包干的这两年人都要废了&#xff0c;技术没一点提升&#xff0c;学不到任何东西&…

【计算机网络笔记】802.11无线局域网

系列文章目录 什么是计算机网络&#xff1f; 什么是网络协议&#xff1f; 计算机网络的结构 数据交换之电路交换 数据交换之报文交换和分组交换 分组交换 vs 电路交换 计算机网络性能&#xff08;1&#xff09;——速率、带宽、延迟 计算机网络性能&#xff08;2&#xff09;…

[ffmpeg] find 编码器

背景 整理 ffmpeg 中&#xff0c;如何通过名字或者 id 找到对应编码器的。 具体流程 搜索函数 avcodec_find_encoder // 通过 ID 搜索编码器 avcodec_find_encoder_by_name // 通过名字搜索编码器源码分析 ffmpeg 中所有支持的编码器都会注册到 codec_list.c 文件中&…

C语言结构体详解(二)(能看懂文字就能明白系列)文章很长,慢慢品尝

系列文章目录 第一章 结构体的介绍和基本使用 &#x1f31f; 个人主页&#xff1a;古德猫宁- &#x1f308; 信念如阳光&#xff0c;照亮前行的每一步 文章目录 系列文章目录&#x1f308; *信念如阳光&#xff0c;照亮前行的每一步* 前言前面一篇文章主要介绍了结构体的基础…

SCAU:字母分类统计

字母分类统计 Time Limit:1000MS Memory Limit:65535K 题型: 编程题 语言: G;GCC;VC 描述 输入一行以换行符结束的字符&#xff0c;统计并输出其中英文字母、数字、空格和其它字符的个数。输入格式 一行字符&#xff0c;以换行符结束输出格式 一行4个数字分别为&#…

[题] The sum problem # 数论 # 因数

题目 The sum problem 题解 参考博客&#xff1a; The sum problem(hdu 2058)解题报告 高斯公式&#xff1a; 12…nn*(n1)/2 sum(a,b)定义为从a到b的总和。 目标&#xff1a;求a, b。 sum(a,b)sum(1,b) –sum(1,a-1) 令ca-1&#xff0c; 代入 sum(a,b)M&#xff0c; 得到 …