当前位置: 首页 > 知识库问答 >
问题:

等待任务。运行与等待

罗河
2023-03-14

我在网上搜索了很多关于任务的问题。Runvs await async,但是在这个特定的使用场景中,我并不真正理解其中的区别。我相信情况很简单。

await Task.Run(() => LongProcess());

vs.

await LongProcess());

其中,LongProcess是一个异步方法,其中包含一些异步调用,例如使用waitExecuteReaderAsync()调用db。

问题:

在这种情况下,两者之间有什么区别吗?任何帮助或意见,谢谢!

共有3个答案

柯苗宣
2023-03-14
匿名用户

这个答案涉及在GUI应用程序的事件处理程序中等待异步方法的特定情况。在这种情况下,第一种方法比第二种方法具有显著优势。在解释原因之前,让我们以一种清晰地反映这个答案的上下文的方式重写这两种方法。以下内容仅与GUI应用程序的事件处理程序相关。

private async void Button1_Click(object sender, EventArgs args)
{
    await Task.Run(async () => await LongProcessAsync());
}

vs.

private async void Button1_Click(object sender, EventArgs args)
{
    await LongProcessAsync();
}

我在方法的名称中添加了后缀Async,以符合指导原则。我还将async作为匿名委托,只是出于可读性的原因。创建状态机的开销是微不足道的,并且与明确传达此任务的值相比相形见绌。Run返回一个promise风格的任务,而不是用于CPU绑定工作负载后台处理的老式委托任务

第一种方法的优点是保证UI保持响应性。第二种方法没有这样的保证。只要您使用的是的内置异步API。NET平台上,UI被第二种方法阻止的可能性非常小。毕竟,这些API是由专家实现的。当您开始等待自己的异步方法时,所有的保证都已失效。当然,除非你的名字是斯蒂芬,你的姓是图布或克利里。如果不是这样,很有可能您迟早会编写这样的代码:

public static async Task LongProcessAsync()
{
    TeenyWeenyInitialization(); // Synchronous
    await SomeBuildInAsyncMethod().ConfigureAwait(false); // Asynchronous
    CalculateAndSave(); // Synchronous
}

问题显然在于方法TeenyWeenyInitialization()。此方法是同步的,位于异步方法主体中第一个await之前,因此不会等待它。每次调用LongProcessAsync()时,它都将同步运行。因此,如果您遵循第二种方法(不使用Task.Run),则TeenyWeenyInitialization()将在UI线程上运行。

这有多糟糕?初始化毕竟很小!只需快速访问数据库获取值,读取小文本文件的第一行,从注册表获取值。几毫秒后就结束了。在你写程序的时候。移动共享驱动器中的数据文件夹之前。在数据库中的数据量变得巨大之前。

但是你可能会很幸运,并且TeenyWeeny初始化()永远保持快速,那么第二个同步方法,计算器和保存()呢?这是在配置为不捕获上下文的wait之后出现的,因此它在线程池线程上运行。它不应该在UI线程上运行,对吗?错。它取决于任务返回的某些构建的InAsyncmethod()。如果任务完成,则不会发生线程切换,并且将在调用该方法的同一线程上运行。如果您遵循第二种方法,这将是UI线程。您可能永远不会遇到这样的情况:在您的开发环境中,YeBuildInAsyncmethod()返回了一个完整的任务,但是正式生产环境可能会有所不同,难以预测。

有一个性能不好的应用程序是令人不愉快的。如果一个应用程序的性能很差,并且冻结了用户界面,那就更糟糕了。你真的想冒险吗?如果没有,请始终使用任务。在事件处理程序中运行(async。尤其是在等待自己编写的方法时!

CharStyle免责声明,某些内置异步API未正确实现

重要提示:任务。Run线程池线程上运行提供的异步委托,因此要求LongProcessAsync与UI线程没有关联。如果它涉及到与UI控件的交互,则任务。运行不是一个选项。感谢@Zmaster在评论中指出了这一重要的微妙之处。

赏新知
2023-03-14

通常人们认为异步等待是由几个线程完成的。事实上,这一切都是由一个线程完成的。

有关此单线程语句,请参见下面的添加内容

帮助我理解AsyncAwait的是EricLippert对AsyncAwait的采访。在中间的某个地方,他和一个等待水煮的厨师进行比较。他没有什么都不做,而是环顾四周,看看是否还有其他事情要做,比如切洋葱。如果这一切都结束了,而水仍然没有沸腾,他会检查是否还有其他事情要做,等等,直到他除了等待之外别无选择。在这种情况下,他会回到他等待的第一件事。

如果您的过程调用了一个可等待的函数,我们可以肯定在这个可等待的函数中的某个地方有一个对可等待函数的调用,否则该函数将不是可等待的。事实上,如果您忘记在可等待函数中的某个地方等待,编译器会警告您。

如果您的等待函数调用另一个等待函数,那么线程将进入该另一个函数并开始执行该函数中的操作,并深入到其他函数中,直到遇到等待。

线程不再等待结果,而是进入调用堆栈,查看是否还有其他代码可以处理,直到看到等待。在调用堆栈中再次上升,处理直到等待,等等。一旦每个人都在等待,线程将查找底部的等待,并在完成后继续。

这样做的好处是,如果等待函数的调用方不需要函数的结果,但可以在需要结果之前执行其他操作,那么这些其他操作可以由线程执行,而不是在函数内部等待。

不立即等待结果的调用如下所示:

private async Task MyFunction()
{
    Task<ReturnType>taskA = SomeFunctionAsync(...)
    // I don't need the result yet, I can do something else
    DoSomethingElse();

    // now I need the result of SomeFunctionAsync, await for it:
    ReturnType result = await TaskA;
    // now you can use object result
}

请注意,在此场景中,所有操作都由一个线程完成。只要你的线程有事情做,他就会很忙。

添加。只涉及一个线程是不正确的。任何无事可做的线程可能会在等待后继续处理您的代码。如果您检查线程id,您可以看到这个id可以在等待之后更改。继续线程具有与原始线程相同的上下文,因此您可以将其视为原始线程。不需要检查Invoke必需,不需要使用互斥体或关键部分。对于您的代码来说,这就好像有一个线程参与其中。

这个答案末尾的文章链接解释了更多关于线程上下文的内容

您将看到等待函数,主要是在其他进程必须执行某些操作的情况下,而您的线程只需等待其他操作完成。例如通过互联网发送数据、保存文件、与数据库通信等。

然而,有时需要进行一些繁重的计算,您希望线程可以自由地执行其他操作,比如响应用户输入。在这种情况下,您可以像调用异步函数一样启动一个等待的操作。

Task<ResultType> LetSomeoneDoHeavyCalculations(...)
{
    DoSomePreparations()
    // start a different thread that does the heavy calculations:
    var myTask = Task.Run( () => DoHeavyCalculations(...))
    // now you are free to do other things
    DoSomethingElse();
    // once you need the result of the HeavyCalculations await for it
    var myResult = await myTask;
    // use myResult
    ...
}

现在,一个不同的线程正在做繁重的计算,而您的线程可以自由地做其他事情。一旦开始等待,你的呼叫者可以做一些事情,直到他开始等待。实际上,您的线程将相当自由地对用户输入做出反应。然而,只有当每个人都在等待时,情况才会如此。当你的线程忙于做一些事情时,你的线程无法对用户输入做出反应。因此,如果你认为你的UI线程必须做一些需要一些时间的繁忙处理,请务必确保使用任务。跑,让另一个线程来做

另一篇帮助我的文章:杰出的解释者斯蒂芬·克利里的《异步等待》

訾稳
2023-03-14

任务。Run可以在不同的线程处发布要处理的操作。这是唯一的区别。

这可能很有用-例如,如果LongProcess不是真正的异步,它将使调用方更快地返回。但是对于一个真正的异步方法,使用任务是没有意义的。运行,可能会导致不必要的浪费。

但是要小心,因为任务的行为。Run将根据重载解决方案进行更改。在您的示例中,Func

 类似资料:
  • 使用asyn/wait vs wait有什么区别task.run() 等待任务。运行示例- 异步等待示例-

  • 在C#中,我有以下两个简单的例子: 第一个示例创建一个打印“开始”的任务,等待5秒钟打印“完成”,然后结束任务。我等待任务完成,然后打印“全部完成”。当我运行测试时,它会按预期运行。 第二个测试应该具有相同的行为,只是由于使用了async和Wait,任务内部的等待应该是非阻塞的。但是这个测试只打印“开始”,然后立即打印“全部完成”和“完成”,永远不会打印。 我不知道我为什么会有这样的行为:S非常感

  • 我只想确保我很好地理解异步await和task.run或task.whenall之间的区别 所以异步等待就是处理异步方法。它意味着隐含着一个处理顺序。 我在不阻塞主线程的情况下运行了一个很长的处理,并等待结果继续。 对于task.run和task.when,这里有一个多线程的新概念。这意味着我可以在一个新线程上启动一个长进程,它不会等待完成来继续代码。代码在新线程上。在这个线程上,我可以等待方法。

  • 我的makefile中的一个目标是一个非常耗时的CPU任务。但是我可以分割工作负载并并行运行任务几次,以加快整个过程。 我的问题是make不会等待所有过程完成。 考虑一下这个简单的脚本,名为“代码> MyTask.SH <代码>: 现在,让我们从bash脚本调用它,并使用等待所有任务完成: 产出如预期: 但是在Makefile中尝试相同的方法时: 它不起作用: 当然,我可以创建多个目标,这些目标可

  • 我试图了解 Task.Run Wait() 异步等待是如何工作的。 我已经阅读了这个页面:了解在一行中使用Task.Run Wait()异步等待的使用,但不太理解它。 在我的代码中,我从Microsoft EventHub接收事件,并使用实现的类处理它们。我在 () 中调用 方法,这是一个异步方法,这是一个方法。由于该方法是的,因此我使用 和来委派。(即 或< code >。Wait()(该方法是

  • 问题内容: 我在项目中将Django与PostgreSQL一起使用。我把它们放在不同的容器中,问题是我需要在运行django之前等待postgres。这时我正在用django容器的command.sh文件执行此操作。我还发现netcat可以解决问题,但是我更喜欢没有附加软件包的方法。curl和wget无法执行此操作,因为它们不支持postgres协议。有办法吗? 问题答案: 您的解决方案tizia