async/await的多线程问题
今天尝试把.net4.5新增的异步编程模型async/await加入自己的框架,因为从第一印象看,使用async/await的写法实在太方便了,以同步代码的方式写异步流程,写起来更顺畅,不容易打断思路,异常传递、资源控制(lock和using)也都完美支持,即使有少量的性能损失,也完全可以接受。
首先我写了一个测试代码,以熟悉async/await模型,代码如下:
static class TestCase
{
static async Task Test2()
{
Console.WriteLine("3 {0}", Thread.CurrentThread.ManagedThreadId);
await Task.Run(() =>
{
Thread.Sleep();
});// 这里可以换成Task.Delay(1000);
Console.WriteLine("4 {0}", Thread.CurrentThread.ManagedThreadId);
} static async Task Test1()
{
Console.WriteLine("1 {0}", Thread.CurrentThread.ManagedThreadId);
await Test2();
Console.WriteLine("2 {0}", Thread.CurrentThread.ManagedThreadId);
} static void Main(string[] args)
{
Test1().Wait();
}
}
输出如下:

代码很简单,结果也看似正确,直到我发现一个大问题:
await后面的代码是在另一个线程执行的!!!
这不仅会影响到框架的运行,还会导致线程争用资源的问题,也就是说看似同一处的代码,会运行在不同线程,并且有可能并行。如果访问公共资源(如静态变量)还需要加锁,严重影响编程体验和性能。我推测,很可能Task类自带的创建Task的静态函数所产生的Task,设置Complete状态时都是在其他线程,因此导致了await的回调也在该线程执行。于是我把代码稍微改了一下:
static class TestCase
{
static TaskCompletionSource<object> source = new TaskCompletionSource<object>(); static async Task Test2()
{
Console.WriteLine("3 {0}", Thread.CurrentThread.ManagedThreadId);
await source.Task;
Console.WriteLine("4 {0}", Thread.CurrentThread.ManagedThreadId);
} static async Task Test1()
{
Console.WriteLine("1 {0}", Thread.CurrentThread.ManagedThreadId);
await Test2();
Console.WriteLine("2 {0}", Thread.CurrentThread.ManagedThreadId);
} static void Main(string[] args)
{
Task task = Test1();
Thread.Sleep();
source.SetResult(null);
task.Wait(); // 这一句其实是没有必要的,并且如果放在SetResult前,会导致死锁
}
}
输出如下:

果然全部是在主线程执行了。其中source.SetResult(null);这一句放到框架中,当条件合适时就在主线程Loop中调用,以便唤醒await继续执行剩下的过程。此外,Task类的静态函数所产生的Task,也可以通过一个包装函数,来让在其他线程执行的SetResult,Queue到主线程调用,类似这样:
public Task<T> Wrap<T>(Task<T> task)
{
Loop loop = Current;
TaskCompletionSource<T> source = new TaskCompletionSource<T>();
task.GetAwaiter().OnCompleted(() =>
{
loop.Execute(() =>
{
if (task.IsCompleted)
source.TrySetResult(task.Result);
else if (task.IsCanceled)
source.TrySetCanceled();
else
source.TrySetException(task.Exception);
});
});
return source.Task;
}
async/await的多线程问题的更多相关文章
- C# Async/await 异步多线程编程
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.N ...
- 多线程(5)async&await
.net 4.0的Task已经让我们可以非常简单地使用多线程,并且可以有返回值,也可以支持线程的取消等操作,可谓已经很强大了.但.net 4.5为我们带来了async&await,使得实现多线 ...
- async await 多线程
async await 并没有开启多线程 出现await的地方 只是开启了一个子线程继续往后执行 主线程返回 防止阻塞 相当于 await customerRepository.getall() ...
- 聊聊多线程那一些事儿 之 五 async.await深度剖析
hello task,咱们又见面啦!!是不是觉得很熟读的开场白,哈哈你哟这感觉那就对了,说明你已经阅读过了我总结的前面4篇关于task的文章,谢谢支持!感觉不熟悉的也没有关系,在文章末尾我会列出前四 ...
- async/await到底该怎么用?如何理解多线程与异步之间的关系?
前言 如标题所诉,本文主要是解决是什么,怎么用的问题,然后会说明为什么这么用.因为我发现很多萌新都会对之类的问题产生疑惑,包括我最初的我,网络上的博客大多知识零散,刚开始看相关博文的时候,就这样.然后 ...
- C#多线程和异步(二)——Task和async/await详解
一.什么是异步 同步和异步主要用于修饰方法.当一个方法被调用时,调用者需要等待该方法执行完毕并返回才能继续执行,我们称这个方法是同步方法:当一个方法被调用时立即返回,并获取一个线程执行该方法内部的业务 ...
- C#多线程和异步(二)——Task和async/await详解(转载)
一.什么是异步 同步和异步主要用于修饰方法.当一个方法被调用时,调用者需要等待该方法执行完毕并返回才能继续执行,我们称这个方法是同步方法:当一个方法被调用时立即返回,并获取一个线程执行该方法内部的业务 ...
- C#多线程和异步——Task和async/await详解
阅读目录 一.什么是异步 二.Task介绍 1 Task创建和运行 2 Task的阻塞方法(Wait/WaitAll/WaitAny) 3 Task的延续操作(WhenAny/WhenAll/Cont ...
- [.NET] 利用 async & await 的异步编程
利用 async & await 的异步编程 [博主]反骨仔 [出处]http://www.cnblogs.com/liqingwen/p/5922573.html 目录 异步编程的简介 异 ...
随机推荐
- 解析jQuery中extend方法--源码解析以及递归的过程《二》
源码解析 在解析代码之前,首先要了解extend函数要解决什么问题,以及传入不同的参数,会达到怎样的效果.extend函数内部处理传入的不同参数,返回处理后的对象. extend函数用来扩展对象,增加 ...
- mybatis的动态sql中collection与assoction
User.java, Role.java,address.java为三个类 public class User { .....//user自己的属性//association一对一 private R ...
- PAT---完美数列
由于数值比较大,选用long型的 先用快速排序方法对数组进行排序,然后进行查找. 用一个问题是所要查找的数可能不在数组中,因此不能用现成的二叉查找法.试着对二叉查找法进行改进,单没调通. 在查找过程中 ...
- C#之系统异常处理机制
在系统开发过程中,BUG和异常产生是无处不在的,但是需要我们去做的就是不断去发掘异常.修改异常. 这篇文章主要谈谈我在系统中解决异常的几种方法: 1.控制台程序产生的异常: 在大多数的控制台程序中,运 ...
- Spring Cache扩展:注解失效时间+主动刷新缓存
*:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !important; } /* ...
- MySQL之乱码问题解决详解
今天在写一个项目的时候,在数据库中手动插入数据不会产生中文乱码,但是通过javaWeb却出现乱码,把提交表单和响应中的乱码问题解决后,还是乱码.所以我锁定一定是我的mysql数据库中出现了乱码的现象.
- KMP算法的优化与详解
文章开头,我首先抄录一些阮一峰先生关于KMP算法的一些讲解. 下面,我用自己的语言,试图写一篇比较好懂的 KMP 算法解释. 1. 首先,字符串"BBC ABCDAB ABCDABCDABD ...
- 提高C++编译速度-------pimpl 模式& 桥接模式(转)
pimpl 模式(Private Implementation),我们常常听到诸如“不要改动你的公有接口”这样的建议,所以我们一般都会修改私有接口,但是这会导致包含该头文件的所有源文件都要重新编译,这 ...
- 扩大按钮 btn 响应区域
方法一:类别 p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px Menlo; color: #c91b13 } p.p2 { margin: 0 ...
- H5 视频
HTML 5 视频 HTML5 简介 HTML5 视频/DOM 许多时髦的网站都提供视频.HTML5 提供了展示视频的标准. 检测您的浏览器是否支持 HTML5 视频: Yes! Full suppo ...