C# 您是否必须将 Task.Run 放在一个方法中以使其异步?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17119075/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 08:39:05  来源:igfitidea点击:

Do you have to put Task.Run in a method to make it async?

c#.net-4.5async-awaitc#-5.0

提问by Neal

I'm trying to understand async await in the simplest form. I want to create a very simple method that adds two numbers for the sake of this example, granted, it's no processing time at all, it's just a matter of formulating an example here.

我试图以最简单的形式理解异步等待。为了这个例子,我想创建一个非常简单的方法,将两个数字相加,当然,它根本没有处理时间,这只是在这里制定一个例子的问题。

Example 1

示例 1

private async Task DoWork1Async()
{
    int result = 1 + 2;
}

Example 2

示例 2

private async Task DoWork2Async()
{
    Task.Run( () =>
    {
        int result = 1 + 2;
    });
}

If I await DoWork1Async()will the code run synchronously or asynchronously?

如果我 awaitDoWork1Async()代码是同步运行还是异步运行?

Do I need to wrap the sync code with Task.Runto make the method awaitable AND asynchronous so as not to block the UI thread?

我是否需要包装同步代码Task.Run以使方法可等待和异步,以免阻塞 UI 线程?

I'm trying to figure out if my method is a Taskor returns Task<T>do I need to wrap the code with Task.Runto make it asynchronous.

我试图弄清楚我的方法是 aTask还是 returnTask<T>我需要用Task.Run它来包装代码以使其异步。

Stupid question I'm sure but I see examples on the net where people are awaiting code that has nothing async within and not wrapped in a Task.Runor StartNew.

愚蠢的问题我敢肯定,但我在网上看到一些例子,人们在等待代码中没有任何异步内容并且没有包含在 a Task.Runor 中StartNew

采纳答案by Stephen Cleary

First, let's clear up some terminology: "asynchronous" (async) means that it may yield control back to the calling thread before it starts. In an asyncmethod, those "yield" points are awaitexpressions.

首先,让我们澄清一些术语:“异步” ( async) 意味着它可能会在调用线程启动之前将控制权交还给调用线程。在一个async方法中,那些“屈服”点是await表达式。

This is very different than the term "asynchronous", as (mis)used by the MSDN documentation for years to mean "executes on a background thread".

这与术语“异步”非常不同,因为 MSDN 文档多年来(错误地)使用它来表示“在后台线程上执行”。

To futher confuse the issue, asyncis very different than "awaitable"; there are some asyncmethods whose return types are not awaitable, and many methods returning awaitable types that are not async.

进一步混淆这个问题,async与“awaitable”有很大不同;有一些async方法的返回类型是不可等待的,而许多方法返回的可等待类型不是async.

Enough about what they aren't; here's what they are:

足够了解它们不是什么;他们是这样的

  • The asynckeyword allows an asynchronous method (that is, it allows awaitexpressions). asyncmethods may return Task, Task<T>, or (if you must) void.
  • Any type that follows a certain pattern can be awaitable. The most common awaitable types are Taskand Task<T>.
  • 所述async关键字允许异步方法(即,它允许await表达式)。async方法可能返回Task, Task<T>, 或 (如果必须的话) void
  • 任何遵循特定模式的类型都可以等待。最常见的可等待类型是TaskTask<T>

So, if we reformulate your question to "how can I run an operation on a background threadin a way that it's awaitable", the answer is to use Task.Run:

因此,如果我们将您的问题重新表述为“我如何以可等待的方式在后台线程上运行操作”,那么答案是使用Task.Run

private Task<int> DoWorkAsync() // No async because the method does not need await
{
  return Task.Run(() =>
  {
    return 1 + 2;
  });
}

(But this pattern is a poor approach; see below).

(但这种模式是一种糟糕的方法;见下文)。

But if your question is "how do I create an asyncmethod that can yield back to its caller instead of blocking", the answer is to declare the method asyncand use awaitfor its "yielding" points:

但是,如果您的问题是“我如何创建一个async可以返回给调用者而不是阻塞的方法”,那么答案是声明该方法asyncawait用于其“屈服”点:

private async Task<int> GetWebPageHtmlSizeAsync()
{
  var client = new HttpClient();
  var html = await client.GetAsync("http://www.example.com/");
  return html.Length;
}

So, the basic pattern of things is to have asynccode depend on "awaitables" in its awaitexpressions. These "awaitables" can be other asyncmethods or just regular methods returning awaitables. Regular methods returning Task/Task<T>canuse Task.Runto execute code on a background thread, or (more commonly) they can use TaskCompletionSource<T>or one of its shortcuts (TaskFactory.FromAsync, Task.FromResult, etc). I don'trecommend wrapping an entire method in Task.Run; synchronous methods should have synchronous signatures, and it should be left up to the consumer whether it should be wrapped in a Task.Run:

因此,事物的基本模式是让async代码依赖于其await表达式中的“awaitables” 。这些“awaitables”可以是其他async方法或只是返回awaitables 的常规方法。常规方法返回Task/Task<T>使用Task.Run到在后台线程执行代码,或(更常见),他们可以用TaskCompletionSource<T>或它的快捷方式(之一TaskFactory.FromAsyncTask.FromResult等等)。我建议将整个方法包装在Task.Run; 同步方法应该具有同步签名,并且应该由消费者决定是否应该将其包装在Task.Run

private int DoWork()
{
  return 1 + 2;
}

private void MoreSynchronousProcessing()
{
  // Execute it directly (synchronously), since we are also a synchronous method.
  var result = DoWork();
  ...
}

private async Task DoVariousThingsFromTheUIThreadAsync()
{
  // I have a bunch of async work to do, and I am executed on the UI thread.
  var result = await Task.Run(() => DoWork());
  ...
}

I have an async/awaitintroon my blog; at the end are some good followup resources. The MSDN docs for asyncare unusually good, too.

我的博客上有一个async/await介绍;最后是一些很好的后续资源。MSDN 文档async也非常好。

回答by Ronald Ramos

One of the most important thing to remember when decorating a method with asyncis that at least there is oneawaitoperator inside the method. In your example, I would translate it as shown below using TaskCompletionSource.

使用async装饰方法时要记住的最重要的事情之一是方法内部至少有一个await运算符。在您的示例中,我将使用TaskCompletionSource如下所示进行翻译

private Task<int> DoWorkAsync()
{
    //create a task completion source
    //the type of the result value must be the same
    //as the type in the returning Task
    TaskCompletionSource<int> tcs = new TaskCompletionSource<int>();
    Task.Run(() =>
    {
        int result = 1 + 2;
        //set the result to TaskCompletionSource
        tcs.SetResult(result);
    });
    //return the Task
    return tcs.Task;
}

private async void DoWork()
{
    int result = await DoWorkAsync();
}

回答by zheng yu

When you use Task.Run to run a method, Task gets a thread from threadpool to run that method. So from the UI thread's perspective, it is "asynchronous" as it doesn't block UI thread.This is fine for desktop application as you usually don't need many threads to take care of user interactions.

当您使用 Task.Run 运行一个方法时,Task 从线程池中获取一个线程来运行该方法。所以从 UI 线程的角度来看,它是“异步的”,因为它不会阻塞 UI 线程。这对于桌面应用程序来说很好,因为您通常不需要很多线程来处理用户交互。

However, for web application each request is serviced by a thread-pool thread and thus the number of active requests can be increased by saving such threads. Frequently using threadpool threads to simulate async operation is not scalable for web applications.

但是,对于 Web 应用程序,每个请求都由线程池线程提供服务,因此可以通过保存此类线程来增加活动请求的数量。经常使用线程池线程来模拟异步操作对于 Web 应用程序来说是不可扩展的。

True Async doesn't necessarily involving using a thread for I/O operations, such as file / DB access etc. You can read this to understand why I/O operation doesn't need threads. http://blog.stephencleary.com/2013/11/there-is-no-thread.html

真正的异步不一定涉及使用线程进行 I/O 操作,例如文件/数据库访问等。您可以阅读本文以了解为什么 I/O 操作不需要线程。http://blog.stephencleary.com/2013/11/there-is-no-thread.html

In your simple example,it is a pure CPU-bound calculation, so using Task.Run is fine.

在您的简单示例中,它是一个纯粹的 CPU 密集型计算,因此使用 Task.Run 很好。