.Net 线程&异步

引子

今天遇到一个简单的问题,一个获取下载文件的接口,本来是要在判断文件不存在的情况下重新生成的,但是因为重新生成需要的时间比较长,因此就考虑,当文件不存在的时候开启一个后台线程,而直接返回错误,让重试,这样体验会好一点。

代码如下

string filePath = "";
if (File.Exists(filePath)) return true;
Task.Run(() =>
{
    // 执行生成文件的操作
});
return false;

这里的Task.Run()就是启动一个后台线程,不会影响主线程,而直接返回失败,这样在用户下一次在点击下载的时候,就可以直接获取到文件了。

开启异步线程(不阻塞线程)的方法

1.使用Thread类

2.使用ThreadPool线程池

3.使用最新的Task

第一种方法已经淘汰,因为这种方法是不会考虑机器的实际情况,会不停的创建线程,最终很容易导致内容溢出;

第二种方法,在Task没有出现的时候经常使用,优点是线程池中会合理管理线程,如果到达上限后,后续创建的线程会进行排队,但是用它来操作线程略显麻烦

第三种是新方法,比较推荐,改善了方法1和2的缺点。

使用方法如下

Task t1 = new Task(()=>{});
t1.Start();

或者

Task.Run(()=> { });

后者相当于将创建的线程直接启动了。

后续执行ContinueWith

可以对后台任务分成若干块,除了第一部分外,以ContinueWith连接,如下

 Task.Run(()=> { }).ContinueWith((t)=> { }).ContinueWith((t) => { });

而参数t就是上一步执行的task,可以在ContinueWith方法对上一步的操作结果进行判断,以进行不同的处理

Task的

IsCompleted属性可以判断任务是否执行完成

IsCompletedSuccessfully属性可以判断任务是否有异常

可以放在ContinueWith中用来判断任务的执行情况

线程同步

Task对象使用Wait()方法来实现线程的等待,多个Task则可以使用Task.WaitAll()或者Task.WaitAny()来同步

wait()和await

为了线程同步有时候需要对线程进行等待,有两种方法,如

Task.Run(()=>{}).Wait()

或者

await Task.Run(()=>{})

这两种方法效果类似,都是将线程等待起来,有结果再继续,但是区别在于wait()是同步阻塞,而await是异步阻塞。

也就是说wait()的时候,线程是占用状态,无法释放

而await的时候,线程是释放的,可以用来做其他的工作

一个现象

经常发现一些朋友在写web项目代码的时候,方法中调用了很多await的异步接口,其实这些异步接口的使用并不能提高程序的性能,也就是说不是说一个接口使用同步接口调用了3秒,换成异步接口就变1秒了(去掉await并行执行另说)。

执行效率还是不会变的,只不过程序在遇到await之后将线程释放,以提高网站的吞吐量。

总结

异步和多线程真的相当复杂,这里只是记录了自己使用中的一些经验,肯定有不足和错误的地方,希望见谅

总之在使用异步和多线程的时候,要比单线程程序更加复杂,一些单线程程序中理所应当的东西,放在多线程异步环境下可能会出现大问题,使用的时候必须要小心和谨慎

原文地址:https://www.cnblogs.com/gamov/p/13215112.html