任务Task和线程Thread的区别:
1、任务是架构在线程之上的,也就是说任务最终还是要抛给线程去执行。
2、任务跟线程不是一对一的关系,比如开10个任务并不是说会开10个线程,这一点任务有点类似线程池,但是任务相比线程池有很小的开销和精确的控制。
Task和Thread一样,位于System.Threading命名空间下!
使用async和await关键字。
async: 当一个方法由async关键字标识,表明这个方法是异步方法,当它被调用时,会创建一个线程来执行。
async 只能修饰用于返回void,Task,Task<>的方法。
async在VS11后不能作为程序的入口点,即Main方法不能使用async修饰符。await修饰符只能用于返回Task的方法。
async和await这两个关键字由编译器转换为状态机,通过System.Threading.Tasks中的类实现异步编程。即async和await关键字只是编译器的功能。编译器最终会用Task类创建代码。
很多文档包括msdn都刻意提到async/await关键字不会创建新的线程,用async关键字写的函数中的代码都在调用线程中执行。这里是最容易混淆的地方,严格意义上这个说法不准确,异步编程必然是多线程的。**msdn文档里提到的不会创建新线程是指async函数本身不会直接在新线程中运行。**通过反编译分析,我们知道本质上是await调用的异步函数执行完成后回调状态机的MoveNext来执行余下未执行完成的代码,await调用的异步函数必然在某个地方——也许是嵌套了很深的一个地方——启动了一个新的工作线程?来完成导致我们要使用异步调用的耗时比较长的工作,比如网络内容读取。
异步方法的返回类型必须为 void、Task、Task 中的其中一种。
void,表示无返回值,不关心异步方法执行后的结果,一般用于仅仅执行某一项任务,但是不关心结果的场景。
Task,表示异步方法将返回一个 Task 对象,该对象通常用于判断异步任务是否已经完成,可以使用 taskObj.Wait() 方法等待,或者 taskObj.IsCompleted 判断。
Task,表示异步方法将返回一个 Task 对象,该对象的 Result 属性则是异步方法的执行结果,调用该属性时将阻塞当前线程(异步方法未执行完成时)。
在使用async/await关键字时,应该遵循一些最佳实践,以提高代码的可读性、可维护性和性能。下面是一些常见的最佳实践:
-
尽可能将异步方法声明为
Task
或Task<TResult>
类型,以便可以使用await关键字等待其完成。如果异步方法不返回任何内容,则应将其声明为Task类型。 -
在异步方法内部尽可能避免使用阻塞线程的操作,而应该使用非阻塞操作来模拟延迟。如果必须执行阻塞操作,可以将其放在不同的线程上执行,或者使用异步IO操作来避免阻塞线程。
-
在异步方法内部不要捕获异常并立即处理,因为这会导致代码变得复杂难以维护。应该让调用者自行处理异常。如果必须在异步方法内部捕获异常,也应该将其包装成
AggregateException
异常,并将其传递给调用者。 -
在使用
ConfigureAwait(false)
方法时要小心,只有在确定不需要返回到原始的SynchronizationContext
上时才使用,否则可能会导致调用者无法正确处理结果。 -
尽量避免在异步方法中使用不安全的线程API,例如
Thread.Sleep
或Thread.Join
等方法,以确保代码的可移植性和稳定性。应该使用非阻塞的异步方法来模拟延迟。 -
在使用async/await关键字时,应该遵循一些命名约定,例如异步方法的名称应该以
Async
结尾,以便于区分同步和异步方法。 -
在需要同时等待多个异步任务完成时,可以使用
Task.WhenAll
方法等待所有任务完成。如果只需要等待其中一个任务完成,则可以使用Task.WhenAny
方法等待任意一个任务完成。 -
在异步方法内部,应该将耗时的操作封装为另外的异步方法,并在需要的地方使用
async/await
关键字调用它们,以提高代码的可读性和可维护性。 -
在使用async/await关键字时,应该尽可能避免使用线程同步机制,例如
lock
关键字或Monitor
类,因为这会导致UI线程被阻塞。而应该使用异步锁或其他非阻塞的线程同步机制。
总之,使用Task和async/await可以大大简化异步编程,提高代码的可读性、可维护性和性能。但是,需要注意一些细节和最佳实践,以确保代码的正确性和稳定性。
原文链接:https://blog.csdn.net/u014677109/article/details/117407617