根据我的理解,async和await所做的主要事情之一是使代码易于编写和阅读-但使用它们是否等于生成后台线程来执行长时间的逻辑?
我目前正在尝试最基本的例子。我内联添加了一些注释。你能给我解释一下吗?
// I don't understand why this method must be marked as `async`.
private async void button1_Click(object sender, EventArgs e)
{
Task<int> access = DoSomethingAsync();
// task independent stuff here
// this line is reached after the 5 seconds sleep from
// DoSomethingAsync() method. Shouldn't it be reached immediately?
int a = 1;
// from my understanding the waiting should be done here.
int x = await access;
}
async Task<int> DoSomethingAsync()
{
// is this executed on a background thread?
System.Threading.Thread.Sleep(5000);
return 1;
}
在更高的层次上:
1) Async关键字启用等待,这就是它所做的一切。Async关键字不会在单独的线程中运行该方法。beginf async方法同步运行,直到它命中一个耗时任务的await。
2)你可以等待一个返回Task或t类型Task的方法。你不能等待async void方法。
3)主线程遇到等待耗时任务或实际工作开始时,主线程返回到当前方法的调用方。
4)如果主线程看到一个仍在执行的任务在等待,它不会等待它,而是返回到当前方法的调用者。通过这种方式,应用程序保持响应性。
5)等待处理任务,现在将在线程池的独立线程上执行。
6)当这个await任务完成时,它下面的所有代码将由单独的线程执行
下面是示例代码。执行它并检查线程id
using System;
using System.Threading;
using System.Threading.Tasks;
namespace AsyncAwaitDemo
{
class Program
{
public static async void AsynchronousOperation()
{
Console.WriteLine("Inside AsynchronousOperation Before AsyncMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
//Task<int> _task = AsyncMethod();
int count = await AsyncMethod();
Console.WriteLine("Inside AsynchronousOperation After AsyncMethod Before Await, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
//int count = await _task;
Console.WriteLine("Inside AsynchronousOperation After AsyncMethod After Await Before DependentMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
DependentMethod(count);
Console.WriteLine("Inside AsynchronousOperation After AsyncMethod After Await After DependentMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
}
public static async Task<int> AsyncMethod()
{
Console.WriteLine("Inside AsyncMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
int count = 0;
await Task.Run(() =>
{
Console.WriteLine("Executing a long running task which takes 10 seconds to complete, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(20000);
count = 10;
});
Console.WriteLine("Completed AsyncMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
return count;
}
public static void DependentMethod(int count)
{
Console.WriteLine("Inside DependentMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId + ". Total count is " + count);
}
static void Main(string[] args)
{
Console.WriteLine("Started Main method, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
AsynchronousOperation();
Console.WriteLine("Completed Main method, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
Console.ReadKey();
}
}
}
我想对此发表我的意见,如果任何其他答案包含我将解释的内容,我很抱歉,我读了大部分,但没有找到它,但我可能错过了一些东西。
我看到了很多错误的概念和很多好的解释,只是想解释一下异步与并行编程的区别,我相信这会让事情更容易理解。
当你需要做长时间的计算,处理器密集的工作,你应该选择使用并行编程,如果可能的话,以优化核心的使用。这将打开一些线程并同时处理一些事情。
假设你有一个数字数组,想要对每一个数字进行一些昂贵的长计算。平行是你的朋友。
异步编程在不同的用例中使用。
当你在等待一些不依赖于你的处理器的事情时,它用来释放你的线程,例如IO(写入和读取磁盘),当你执行IO时,你的线程什么都不做,当你等待一个昂贵的查询结果从DB返回时也是一样。
异步方法在线程等待很长时间返回结果时释放线程。这个线程可以被应用程序的其他部分使用(例如,在web应用程序中,它可以处理其他请求),也可以返回操作系统用于其他用途。
当您的结果完成时,相同的线程(或另一个线程)将返回给您的应用程序以继续处理。
在像。net这样的多线程环境中,异步编程不是强制性的(但是个很好的实践),在web应用程序中,其他线程将响应新请求,但如果你是在像nodejs这样的单线程框架中,这是强制性的,因为你不能阻塞你唯一的线程,否则你将无法回答任何其他请求。
总而言之,长处理器密集型计算将从并行编程中受益更多,而不依赖于处理器的长等待时间,如IO或DB查询或对某些API的调用将从异步编程中受益更多。
这就是为什么Entity Framework有一个async api来保存、列表、查找等等…
记住async/await与wait或waitAll不同,上下文不同。Async/await释放线程,是异步编程。wait / waitAll阻塞所有线程(它们没有被释放)来强制并行上下文中的同步…不同的东西…
希望这对某些人有用…
在一个简单的控制台程序中显示上述解释:
class Program
{
static void Main(string[] args)
{
TestAsyncAwaitMethods();
Console.WriteLine("Press any key to exit...");
Console.ReadLine();
}
public async static void TestAsyncAwaitMethods()
{
await LongRunningMethod();
}
public static async Task<int> LongRunningMethod()
{
Console.WriteLine("Starting Long Running method...");
await Task.Delay(5000);
Console.WriteLine("End Long Running method...");
return 1;
}
}
输出为:
Starting Long Running method...
Press any key to exit...
End Long Running method...
因此,
Main通过TestAsyncAwaitMethods启动长时间运行的方法。这立即返回,而不停止当前线程,我们立即看到'按任何键退出'消息
在此期间,LongRunningMethod一直在后台运行。一旦它完成,来自Threadpool的另一个线程拾取该上下文并显示最终消息
因此,没有线程被阻塞。