简体   繁体   English

在.NET中,异步方法是否只使用async关键字?

[英]In .NET, are async methods exclusively those with the async keyword?

I've been learning about HttpClient (consuming API's in .NET in general), and therefore about async programming. 我一直在学习HttpClient(一般在.NET中使用API​​),因此关于异步编程。 I'm still pretty lost right now, so trying to clear some things up. 我现在还很迷茫,所以试图澄清一些事情。 One question I can't seem to find an answer to - are asynchronous methods implemented exclusively using the async keyword? 我似乎无法找到答案的一个问题 - 是使用async关键字专门实现的异步方法吗?

I understand that you could (theoretically) create synchronous methods using async programming, but how do you recognize that? 我知道你可以(理论上)使用异步编程创建同步方法,但是你如何认识到这一点? For example, from this link and this example: 例如,从此链接和此示例:

public string GetReleases(string url)
{
    using (var httpClient = new HttpClient())
    {
        httpClient.DefaultRequestHeaders.Add(RequestConstants.UserAgent, RequestConstants.UserAgentValue);

        var response = httpClient.GetStringAsync(new Uri(url)).Result;

        return response;
    }
}

The author says: 作者说:

For the simplicity's sake, I implemented it synchronously 为了简单起见,我同步实现了它

But how do I recognize it is synchronous? 但我怎么认识到它是同步的? Is it solely because the method is not defined with async Task, for example: 是否仅仅因为该方法未使用异步任务定义,例如:

public async Task<string> GetReleases(string url)

Does that mean that in this HttpClient tutorial , this example is also not asynchronous: 这是否意味着在此HttpClient教程中 ,此示例也不是异步的:

// GET: Student
    public ActionResult Index()
    {
        IEnumerable<StudentViewModel> students = null;

        using (var client = new HttpClient())
        {
            client.BaseAddress = new Uri("http://localhost:64189/api/");
            //HTTP GET
            var responseTask = client.GetAsync("student");
            responseTask.Wait();

            var result = responseTask.Result;
            if (result.IsSuccessStatusCode)
            {
                var readTask = result.Content.ReadAsAsync<IList<StudentViewModel>>();
                readTask.Wait();

                students = readTask.Result;
            }
            else //web api sent error response 
            {
                //log response status here..

                students = Enumerable.Empty<StudentViewModel>();

                ModelState.AddModelError(string.Empty, "Server error. Please contact administrator.");
            }
        }
        return View(students);
    }

So to sum up, my questions are: 总而言之,我的问题是:

  1. Is async programming (in .NET), and async methods, implemented exclusively using async and void/Task/Task ? 异步编程(在.NET中)和异步方法是否只使用asyncvoid / Task / Task实现
  2. If not, how else, and if so, how do I recognize "true" asynchronous methods compared to synchronous methods implemented using asychronous principles (like the example above?) 如果不是,那么如何,如果是这样,与使用异步原理实现的同步方法相比,如何识别“真正的”异步方法(如上例所示?)
  3. Why then did the above examples use " sync through async ", since from everything I've read everyone says to NEVER do that? 那么为什么上面的例子使用“ 通过异步同步 ”,因为从我读过的所有内容中,每个人都说永远不会这样做? Are they just bad examples, or for simplicity sake (even so, shouldn't "the correct way" > "simplicity"? Would it be ok to use this in situations where, eg, I have very simple code and want to avoid threading/deadlock issues, since I'm not comfortable with async programming just yet? 它们只是不好的例子,或者为了简单起见(即便如此,不应该“正确的方式”>“简单”?在例如我有非常简单的代码并且想要避免线程的情况下使用它是否可以? /死锁问题,因为我对异步编程感到不舒服吗?

how can I recognize truely async/sync methods? 如何识别真正的异步/同步方法?

You can't. 你不能。 Not really. 并不是的。 You can spot methods that are potentially async, but little else can be learned without consulting documentation or the implementation of those methods. 您可以发现可能异步的方法,但是如果没有咨询文档或这些方法的实现,就很难学到其他方法。

So, you can examine a method's return type. 因此,您可以检查方法的返回类型。 If it's void , you don't know much. 如果它void ,你就不太了解。 If it's Task , Task<T> , ValueTask<T> or any other awaitable type 1 , then the method may be asynchronous. 如果它是TaskTask<T>ValueTask<T>任何其他等待类型 1 ,则该方法可以是异步的。 But bear in mind, the method signature may be fixed because the type inherited the method from a base class or it's implementing an interface; 但请记住,方法签名可能是固定的,因为类型从基类继承了方法或者它正在实现一个接口; So whilst the method has the potential to be async, the actual implementation of that method may be completely synchronous. 因此,虽然该方法可能是异步的,但该方法的实际实现可能是完全同步的。

Or, the method may have the potential to be asynchronous but may have particular control flows which lead to it behaving synchronously 2 . 或者,该方法可能具有异步的潜力 ,但可能具有导致其同步运行的特定控制流2 These may be eg that if certain conditions are true, the method already has the answer, so it returns it straight away. 这些可能是例如,如果某些条件为真,则该方法已经具有答案,因此它立即返回。 Otherwise it goes off and does something async - as one example. 否则它就会熄灭并做一些异步 - 就像一个例子。

If the return type isn't awaitable and it's non-void, all you can actually reason about the method is that, at the point at which it returns, it's supplied some value for that return type 3 . 如果返回类型不是等待的并且它是非空的,那么你实际可以推断出的方法是,在它返回的时候,它为该返回类型3提供了一些值。 There's no way to reason about any other work that may have been started by that method - only that if it's done something like that, it doesn't intend for you to be able to discover when that work has completed. 没有办法推理可能已经通过该方法启动的任何其他工作 - 只有当它完成了类似的工作时,它并不打算让您能够发现该工作何时完成。


If you're looking at the implementation of a method and asking yourself "is this implementation async" then the important thing is to work out what this code makes happen after control is returned back to the caller. 如果您正在查看方法的实现并且问自己“这个实现是否异步”,那么重要的是找出在将控制权返回给调用者之后该代码发生了什么。

When is control returned back to the caller? 控件什么时候返回给调用者?

  • When we hit a return 当我们return
  • When we hit an await , maybe 当我们await也许吧

When we hit an await , we only return control back to the caller 4 if the awaitable that we're await ing isn't complete yet. 当我们await ,如果我们await尚未完成,我们只将控制权返还给呼叫者4 So we have to find out where that awaitable came from and, if it came from calling another method, we have to start again from scratch in considering what that method does. 因此,我们必须找出等待的地方,如果它来自于调用另一种方法,我们必须从头开始重新考虑该方法的作用。

If the method contains await s then it's usually safest to say that it's potentially async - but bear in mind the above possibilities about already completed awaitables and early return s. 如果该方法包含await s那么通常最安全的说它可能是异步的 - 但请记住上述可能已经完成的等待和早期return的可能性。

If it's not async / await , what else might it have done? 如果它不是async / await ,它还能做什么? Well, if it's working with Task s, it may have created one or more of those tasks to represent it's ongoing work. 好吧,如果它正在使用Task ,它可能已经创建了一个或多个任务来表示它正在进行的工作。 It may have scheduled more code to run via ContinueWith . 它可能已安排更多代码通过ContinueWith运行。

If it's not working with Task s directly, hasn't called something else that is potentially async, hasn't cause a new Thread to be created and isn't needlessly obfuscated, it's probably synchronous. 如果它没有直接使用Task s,没有调用可能异步的其他东西,没有导致创建新的Thread并且不会被不必要地混淆,它可能是同步的。


Would it be ok to use this in situations where, eg, I have very simple code and want to avoid threading/deadlock issues, since I'm not comfortable with async programming just yet? 是否可以在例如我有非常简单的代码并且想要避免线程/死锁问题的情况下使用它,因为我对异步编程感到不舒服?

The sync over async patterns shown in the examples in your question are more prone to deadlocks than working with async / await . 与使用async / await相比,问题示例中显示的同步异步模式容易出现死锁。 Why? 为什么? Because they block the current thread waiting for other things to happen. 因为它们会阻止当前线程等待其他事情发生。 But the current thread or resources that it has locked may be special - and the actual async tasks that it's invoked may need to gain access to that same thread/resource before they can complete. 但是它锁定的当前线程或资源可能是特殊的 - 并且它所调用的实际异步任务可能需要在完成之前获得对同一线程/资源的访问权限。 Classic deadlock. 经典的僵局。


1 Awaitable is one of a number of places where C# uses "duck typing". 1 Awaitable是C#使用“duck typing”的众多地方之一。 If there's a method available (instance or extension) called GetAwaiter that returns a type with the right shape, it's awaitable. 如果有一个名为GetAwaiter的方法可用(实例或扩展名)返回一个具有正确形状的类型,那么它是等待的。 So despite the fact that you'll probably never see one, be aware that custom awaitables are possible in the language. 因此,尽管您可能永远不会看到一个,但请注意,语言中可能存在自定义等待。

2 "Hot path" optimizations come to mind. 2 “热门路径”优化浮现在脑海中。

3 And out / ref parameters. 3out / ref参数。 Of course, if it has those it won't be an async method implemented via the async / await keywords but it may still have some asynchronous behaviour. 当然,如果有那些它不会是通过 async / await关键字实现的异步方法,但它可能仍然有一些异步行为。

4 If we've already yielded control back to the caller during an earlier await , we won't return control back to the caller at later await s, but we'll be returning it to "something" that isn't our code. 4如果我们已经在较早的await期间已经将控制权交还给调用者,我们将不会在稍后的await将控制权返回给调用者,但是我们将把它返回到不是我们代码的“某事”。

An asynchronous method in C# can return void , Task or Task<T> , where void should be avoided in general because it cannot be awaited. C#中的异步方法可以返回voidTaskTask<T> ,其中一般应该避免使用void ,因为无法等待它。

As a convention asynchronous methods should be called DoSomethingAsync() 作为惯例,异步方法应该被称为DoSomethingAsync()

The async keyword, however, is an implementation detail and does not belong to the API. 但是, async关键字是一个实现细节,不属于API。 This keyword is only required if you use an await in the method body. 只有在方法体中使用await ,才需要此关键字。 But that need not be the case. 但事实并非如此。 You could simply delegate to another asynchronous method, without the need to mark the method as async and using an await . 您可以简单地委托另一个异步方法,而无需将该方法标记为async并使用await

So 'true' asynchronous methods should be recognizable by the Async suffix of the method name, however, you can't be sure the implementor actually uses naturally asynchronous operations or even runs synchronously some parts or the whole method. 因此,'true'异步方法应该可以通过方法名称的Async后缀来识别,但是,您不能确定实现者实际上是使用自然异步操作,还是同步运行某些部分或整个方法。

In the example he made the method synchronously by putting .Result at the end of GetStringAsync 在示例中,他通过将.Result放在GetStringAsync的末尾来同步创建该方法

  1. If a method returns a Task or Task<T> (with exception to void in case of event handler) then it can be awaited and hence it can be asynchronous. 如果一个方法返回一个TaskTask<T> (在事件处理程序的情况下为void),则可以等待它,因此它可以是异步的。 async keyword is only an indication that it may await somewhere for another awaitable. async关键字只表明它可能await某个地方await另一个人。 Based on the control flow, async may return without actually awaiting anything. 根据控制流程, async可能会返回而不会实际等待任何事情。 Asynchronous programming is not a new thing, it has existed in many forms like callbacks, Invokes etc. 异步编程并不是一件新事物,它以回调,调用等多种形式存在。

  2. Examples you have provided are not using async await pattern properly. 您提供的示例未正确使用异步等待模式。 Microsoft has provided naming convention (Async Suffix) and Task<T> , Task as types for async programming. Microsoft提供了命名约定(Async Suffix)和Task<T>Task作为异步编程的类型。 So if you see some method returning Task<T> or Task and Method name has suffix "Async" then you can consider it asynchronous. 因此,如果您看到一些方法返回Task<T>Task和方法名称后缀为“Async”,那么您可以将其视为异步。 Although suffix thing is not required by compiler, it helps in differentiating it from its synchronous counterpart. 虽然编译器不需要后缀,但它有助于区分它与同步对应物。 ( Read vs ReadAsync ) ReadReadAsync

  3. They are bad examples, those action should be marked as async and all the Async methods should be awaited for result. 它们是不好的例子,这些操作应该标记为async,并且应该等待所有Async方法的结果。 There can be an exception in some console program where main can't be async. 某些控制台程序中可能存在异常,其中main不能异步。

Please read Stephen Cleary blog on async await 请阅读异常等待的Stephen Cleary博客

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM