简体   繁体   English

在 Unity/C# 中,.Net 的 async/await 是否真的启动了另一个线程?

[英]In Unity / C#, does .Net's async/await start, literally, another thread?

Important for anyone researching this difficult topic in Unity specifically ,对于在 Unity 中专门研究这个困难主题的人来说很重要

be sure to see another question I asked which raised related key issues:一定要看到我问的另一个问题,其中提出了相关的关键问题:

In Unity specifically, "where" does an await literally return to? 特别是在 Unity 中,await 真正返回到“哪里”?


For C# experts, Unity is single-threaded 1对于 C# 专家来说,Unity 是单线程的1

It's common to do calculations and such on another thread.在另一个线程上进行计算等是很常见的。

When you do something on another thread, you often use async/wait since, uh, all the good C# programmers say that's the easy way to do that!当你在另一个线程上做某事时,你经常使用 async/wait 因为,呃,所有优秀的 C# 程序员都说这是做到这一点的简单方法!

void TankExplodes() {

    ShowExplosion(); .. ordinary Unity thread
    SoundEffects(); .. ordinary Unity thread
    SendExplosionInfo(); .. it goes to another thread. let's use 'async/wait'
}

using System.Net.WebSockets;
async void SendExplosionInfo() {

    cws = new ClientWebSocket();
    try {
        await cws.ConnectAsync(u, CancellationToken.None);
        ...
        Scene.NewsFromServer("done!"); // class function to go back to main tread
    }
    catch (Exception e) { ... }
}

OK, so when you do this, you do everything "just as you normally do" when you launch a thread in a more conventional way in Unity/C# (so using Thread or whatever or letting a native plugin do it or the OS or whatever the case may be).好的,所以当你这样做时,当你在 Unity/C#中以更传统的方式启动一个线程时,你会“像往常一样”做所有事情(所以使用 Thread 或其他任何东西或让本机插件或操作系统或任何东西来做它)情况可能是)。

Everything works out great.一切都很好。

As a lame Unity programmer who only knows enough C# to get to the end of the day, I have always assumed that the async/await pattern above literally launches another thread .作为一个跛脚的 Unity 程序员,他只知道足够的 C# 来完成一天的工作,我一直认为上面的 async/await 模式实际上会启动另一个线程

In fact, does the code above literally launch another thread , or does c#/.Net use some other approach to achieve tasks when you use the natty async/wait pattern?事实上,当您使用整洁的异步/等待模式时,上面的代码是否真的启动了另一个线程,或者 c#/.Net 是否使用其他一些方法来实现任务?

Maybe it works differently or specifically in the Unity engine from "using C# generally"?也许它在 Unity 引擎中的工作方式与“一般使用 C#”不同或特别适用? (IDK?) (IDK?)

Note that in Unity, whether or not it is a thread drastically affects how you have to handle the next steps.请注意,在 Unity 中,它是否是一个线程会极大地影响您必须如何处理后续步骤。 Hence the question.因此这个问题。


Issue: I realize there's lots of discussion about "is await a thread", but, (1) I have never seen this discussed / answered in the Unity setting (does it make any difference? IDK?) (2) I simply have never seen a clear answer!问题:我意识到有很多关于“正在等待线程”的讨论,但是,(1) 我从来没有在 Unity 设置中看到过这个讨论/回答(这有什么不同吗?IDK?)(2)我从来没有看到一个明确的答案!


1 Some ancillary calculations (eg, physics etc) are done on other threads, but the actual "frame based game engine" is one pure thread. 1一些辅助计算(例如物理等)是在其他线程上完成的,但实际的“基于帧的游戏引擎”是一个纯线程。 (It's impossible to "access" the main engine frame thread in any way whatsoever: when programming, say, a native plugin or some calculation on another thread, you just leave markers and values for the components on the engine frame thread to look at and use when they run each frame.) (以任何方式“访问”主引擎框架线程都是不可能的:在编程时,例如,在另一个线程上编写本机插件或某些计算时,您只需在引擎框架线程上为组件留下标记和值即可查看和在运行每一帧时使用。)

This reading: Tasks are (still) not threads and async is not parallel might help you understand what's going on under the hood.这篇阅读: 任务(仍然)不是线程并且异步不是并行可能会帮助您了解幕后发生的事情。 In short in order for your task to run on a separate thread you need to call简而言之,为了让您的任务在单独的线程上运行,您需要调用

Task.Run(()=>{// the work to be done on a separate thread. }); 

Then you can await that task wherever needed.然后你可以在任何需要的地方等待该任务。

To answer your question回答你的问题

"In fact, does the code above literally launch another thread, or does c#/.Net use some other approach to achieve tasks when you use the natty async/wait pattern?" “事实上,当您使用整洁的异步/等待模式时,上面的代码是否真的启动了另一个线程,或者 c#/.Net 是否使用其他一些方法来实现任务?”

No - it doesn't.不 - 它没有。

If you did如果你做了

await Task.Run(()=> cws.ConnectAsync(u, CancellationToken.None));

Then cws.ConnectAsync(u, CancellationToken.None) would run on a separate thread.然后cws.ConnectAsync(u, CancellationToken.None)将在单独的线程上运行。

As an answer to the comment here is the code modified with more explanations:作为对这里评论的回答,修改了更多解释的代码:

    async void SendExplosionInfo() {

        cws = new ClientWebSocket();
        try {
            var myConnectTask = Task.Run(()=>cws.ConnectAsync(u, CancellationToken.None));

            // more code running...
await myConnectTask; // here's where it will actually stop to wait for the completion of your task. 
            Scene.NewsFromServer("done!"); // class function to go back to main tread
        }
        catch (Exception e) { ... }
    }

You might not need it on a separate thread though because the async work you're doing is not CPU bound (or so it seems).您可能不需要在单独的线程上使用它,因为您正在执行的异步工作不受 CPU 限制(或者看起来如此)。 Thus you should be fine with因此你应该没问题

 try {
            var myConnectTask =cws.ConnectAsync(u, CancellationToken.None);

            // more code running...
await myConnectTask; // here's where it will actually stop to wait for the completion of your task. 
            Scene.NewsFromServer("done!"); // continue from here
        }
        catch (Exception e) { ... }
    }

Sequentially it will do exactly the same thing as the code above but on the same thread.按顺序它会做与上面代码完全相同的事情,但在同一个线程上。 It will allow the code after " ConnectAsync " to execute and will only stop to wait for the completion of " ConnectAsync " where it says await and since " ConnectAsync " is not CPU bound you (making it somewhat parallel in a sense of the work being done somewhere else ie networking) will have enough juice to run your tasks on, unless your code in "...." also requires a lot of CPU bound work, that you'd rather run in parallel.它将允许“ ConnectAsync ”之后的代码执行,并且只会停止等待“ ConnectAsync ”的完成,它说await并且因为“ ConnectAsync ”不是CPU绑定你(从某种意义上说,它有点并行在其他地方完成,即网络)将有足够的果汁来运行您的任务,除非您在“....”中的代码也需要大量 CPU 绑定工作,您宁愿并行运行。

Also you might want to avoid using async void for it's there only for top level functions.此外,您可能希望避免使用async void,因为它仅用于顶级函数。 Try using async Task in your method signature.尝试在您的方法签名中使用async Task You can read more on this here.您可以在此处阅读更多相关信息。

No, async/await does not mean - another thread.不,异步/等待并不意味着 - 另一个线程。 It can start another thread but it doesn't have to.它可以启动另一个线程,但不是必须的。

Here you can find quite interesting post about it: https://blogs.msdn.microsoft.com/benwilli/2015/09/10/tasks-are-still-not-threads-and-async-is-not-parallel/在这里你可以找到关于它的非常有趣的帖子: https : //blogs.msdn.microsoft.com/benwilli/2015/09/10/tasks-are-still-not-threads-and-async-is-not-parallel/

Important notice重要通知

First of all, there's an issue with your question's first statement.首先,你的问题的第一个陈述有问题。

Unity is single-threaded Unity 是单线程的

Unity is not single-threaded; Unity不是单线程的; in fact, Unity is a multi-threaded environment.实际上,Unity 是一个多线程环境。 Why?为什么? Just go to the official Unity web page and read there:只需转到官方 Unity 网页并在那里阅读:

High-performance multithreaded system: Fully utilize the multicore processors available today (and tomorrow), without heavy programming.高性能多线程系统:充分利用当今(和未来)可用的多核处理器,无需大量编程。 Our new foundation for enabling high-performance is made up of three sub-systems: the C# Job System, which gives you a safe and easy sandbox for writing parallel code;我们实现高性能的新基础由三个子系统组成:C# 作业系统,它为您提供了一个安全且简单的沙箱来编写并行代码; the Entity Component System (ECS), a model for writing high-performance code by default, and the Burst Compiler, which produces highly-optimized native code.实体组件系统 (ECS),默认情况下编写高性能代码的模型,以及生成高度优化的本机代码的 Burst 编译器。

The Unity 3D engine uses a .NET Runtime called "Mono" which is multi-threaded by its nature. Unity 3D 引擎使用称为“Mono”的 .NET 运行时,它本质上多线程的。 For some platforms, the managed code will be transformed into native code, so there will be no .NET Runtime.对于某些平台,托管代码将转换为本机代码,因此不会有 .NET Runtime。 But the code itself will be multi-threaded anyway.但无论如何,代码本身将是多线程的。

So please, don't state misleading and technically incorrect facts.因此,请不要陈述误导性和技术上不正确的事实。

What you're arguing with, is simply a statement that there is a main thread in Unity which processes the core workload in a frame-based way .您所争论的只是一个声明,即Unity有一个主线程,它以基于帧的方式处理核心工作负载 This is true.这是真实的。 But it isn't something new and unique!但它不是新的和独特的东西! Eg a WPF application running on .NET Framework (or .NET Core starting with 3.0) has a main thread too (often called the UI thread ), and the workload is processed on that thread in a frame-based way using the WPF Dispatcher (dispatcher queue, operations, frames etc.) But all this doesn't make the environment single-threaded!例如,在 .NET Framework(或从 3.0 开始的 .NET Core)上运行的 WPF 应用程序也有一个主线程(通常称为UI 线程),并且工作负载在该线程上使用 WPF Dispatcher程序以基于帧的方式进行处理(调度器队列、操作、帧等)但是所有这些都不会使环境成为单线程的! It's just a way to handle the application's logic.这只是处理应用程序逻辑的一种方式。


An answer to your question回答你的问题

Please note: my answer only applies to such Unity instances that run a .NET Runtime environment (Mono).请注意:我的回答仅适用于运行 .NET 运行时环境 (Mono) 的此类 Unity 实例。 For those instances that convert the managed C# code into native C++ code and build/run native binaries, my answer is most probably at least inaccurate.对于将托管 C# 代码转换为本机 C++ 代码并构建/运行本机二进制文件的那些实例,我的回答很可能至少是不准确的。

You write:你写:

When you do something on another thread, you often use async/wait since, uh, all the good C# programmers say that's the easy way to do that!当你在另一个线程上做某事时,你经常使用 async/wait 因为,呃,所有优秀的 C# 程序员都说这是做到这一点的简单方法!

The async and await keywords in C# are just a way to use the TAP ( Task-Asynchronous Pattern ). C# 中的asyncawait关键字只是使用 TAP( 任务异步模式)的一种方式。

The TAP is used for arbitrary asynchronous operations. TAP 用于任意异步操作。 Generally speaking, there is no thread .一般来说,没有线程 I strongly recommend to read this Stephen Cleary's article called "There is no thread" .我强烈建议阅读Stephen Cleary 的这篇名为“没有线程”的文章 (Stephen Cleary is a renowned asynchronous programming guru if you don't know.) (如果您不知道,Stephen Cleary 是著名的异步编程大师。)

The primary cause for using the async/await feature is an asynchronous operation.使用async/await功能的主要原因是异步操作。 You use async/await not because "you do something on another thread", but because you have an asynchronous operation you have to wait for.您使用async/await不是因为“您在另一个线程上做某事”,而是因为您有一个必须等​​待的异步操作。 Whether there is a background thread this operation will run or or not - this does not matter for you (well, almost; see below).无论是否有后台线程,此操作将运行与否 - 这对您来说无关紧要(好吧,几乎;见下文)。 The TAP is an abstraction level that hides these details. TAP 是一个隐藏这些细节的抽象层。

In fact, does the code above literally launch another thread , or does c#/.Net use some other approach to achieve tasks when you use the natty async/wait pattern?事实上,当您使用整洁的异步/等待模式时,上面的代码是否真的启动了另一个线程,或者 c#/.Net 是否使用其他一些方法来实现任务?

The correct answer is: it depends .正确答案是:视情况而定

  • if ClientWebSocket.ConnectAsync throws an argument validation exception right away (eg an ArgumentNullException when uri is null), no new thread will be started如果ClientWebSocket.ConnectAsync抛出参数验证异常(例如,当uri为 null 时抛出ArgumentNullException ),则不会启动新线程
  • if the code in that method completes very quickly, the result of the method will be available synchronously, no new thread will be started如果该方法中的代码完成得非常快,则该方法的结果将同步可用,不会启动新线程
  • if the implementation of the ClientWebSocket.ConnectAsync method is a pure asynchronous operation with no threads involved, your calling method will be "suspended" (due to await ) - so no new thread will be started如果ClientWebSocket.ConnectAsync方法的实现是不涉及线程的纯异步操作,则您的调用方法将被“挂起”(由于await ) - 因此不会启动新线程
  • if the method implementation involves threads and the current TaskScheduler is able to schedule this work item on a running thread pool thread, no new thread will be started;如果方法实现涉及线程,并且当前TaskScheduler能够在正在运行的线程池线程上调度此工作项,则不会启动新线程 instead, the work item will be queued on an already running thread pool thread相反,工作项将在已经运行的线程池线程上排队
  • if all thread pool threads are already busy, the runtime might spawn new threads depending on its configuration and current system state, so yes - a new thread might be started and the work item will be queued on that new thread如果所有线程池线程都已忙,则运行时可能会根据其配置和当前系统状态生成新线程,所以是的 -可能会启动一个新线程,并且工作项将在该新线程上排队

You see, this is pretty much complex.你看,这非常复杂。 But that's exactly the reason why the TAP pattern and the async/await keyword pair were introduced into C#.但这正是将 TAP 模式和async/await关键字对引入 C# 的原因。 These are usually the things a developer doesn't want to bother with, so let's hide this stuff in the runtime/framework.这些通常是开发人员不想打扰的事情,所以让我们将这些东西隐藏在运行时/框架中。

@agfc states a not quite correct thing: @agfc 陈述了一个不太正确的事情:

"This won't run the method on a background thread" “这不会在后台线程上运行该方法”

await cws.ConnectAsync(u, CancellationToken.None);

"But this will" “但这会”

await Task.Run(()=> cws.ConnectAsync(u, CancellationToken.None));

If ConnectAsync 's synchronous part implementation is tiny, the task scheduler might run that part synchronously in both cases.如果ConnectAsync的同步部分实现很小,则任务调度程序可能会在两种情况下同步运行该部分。 So these both snippets might be exactly the same depending on the called method implementation.因此,这两个片段可能完全相同,具体取决于调用的方法实现。

Note that the ConnectAsync has an Async suffix and returns a Task .请注意, ConnectAsync有一个Async后缀并返回一个Task This is a convention-based information that the method is truly asynchronous.这是一个基于约定的信息,表明该方法是真正异步的。 In such cases, you should always prefer await MethodAsync() over await Task.Run(() => MethodAsync()) .在这种情况下,您应该始终更喜欢await MethodAsync()不是await Task.Run(() => MethodAsync())

Further interesting reading:进一步有趣的阅读:

I don't like answering my own question, but as it turns out none of the answers here is totally correct.我不喜欢回答我自己的问题,但事实证明这里没有一个答案是完全正确的。 (However many/all of the answers here are hugely useful in different ways). (然而,这里的许多/所有答案在不同方面都非常有用)。

In fact, the actual answer can be stated in a nutshell:事实上,实际的答案可以简而言之:

On which thread the execution resumes after an await is controlled by SynchronizationContext.Current .在等待之后恢复执行的线程由SynchronizationContext.Current控制。

That's it.而已。

Thus in any particular version of Unity (and note that, as of writing 2019, they are drastically changing Unity - https://unity.com/dots ) - or indeed any C#/.Net environment at all - the question on this page can be answered properly.因此,在任何特定版本的 Unity(并注意,截至 2019 年,他们正在彻底改变 Unity - https://unity.com/dots ) - 或者实际上任何 C#/.Net 环境 - 此页面上的问题可以正确回答。

The full information emerged at this follow-up QA:本次后续 QA 中出现了完整信息:

https://stackoverflow.com/a/55614146/294884 https://stackoverflow.com/a/55614146/294884

The code after an await will continue on another threadpool thread.等待之后的代码将在另一个线程池线程上继续。 This can have consequences when dealing with non-thread-safe references in a method, such as a Unity, EF's DbContext and many other classes, including your own custom code.在处理方法中的非线程安全引用时,这可能会产生后果,例如 Unity、EF 的 DbContext 和许多其他类,包括您自己的自定义代码。

Take the following example:以下面的例子为例:

    [Test]
    public async Task TestAsync()
    {
        using (var context = new TestDbContext())
        {
            Console.WriteLine("Thread Before Async: " + Thread.CurrentThread.ManagedThreadId.ToString());
            var names = context.Customers.Select(x => x.Name).ToListAsync();
            Console.WriteLine("Thread Before Await: " + Thread.CurrentThread.ManagedThreadId.ToString());
            var result = await names;
            Console.WriteLine("Thread After Await: " + Thread.CurrentThread.ManagedThreadId.ToString());
        }
    }

The output:输出:

------ Test started: Assembly: EFTest.dll ------

Thread Before Async: 29
Thread Before Await: 29
Thread After Await: 12

1 passed, 0 failed, 0 skipped, took 3.45 seconds (NUnit 3.10.1).

Note that code before and after the ToListAsync is running on the same thread.请注意, ToListAsync之前和之后的代码在同一线程上运行。 So prior to awaiting any of the results we can continue processing, though the results of the async operation will not be available, just the Task that is created.所以在等待任何结果之前,我们可以继续处理,尽管异步操作的结果将不可用,只有创建的Task可用。 (which can be aborted, awaited, etc.) Once we put an await in, the code following will be effectively split off as a continuation, and will/can come back on a different thread. (可以中止、等待等)一旦我们放入了一个await ,接下来的代码将被有效地拆分为一个延续,并且将/可以在不同的线程上返回。

This applies when awaiting for an async operation in-line:这适用于在线等待异步操作时:

    [Test]
    public async Task TestAsync2()
    {
        using (var context = new TestDbContext())
        {
            Console.WriteLine("Thread Before Async/Await: " + Thread.CurrentThread.ManagedThreadId.ToString());
            var names = await context.Customers.Select(x => x.Name).ToListAsync();
            Console.WriteLine("Thread After Async/Await: " + Thread.CurrentThread.ManagedThreadId.ToString());
        }
    }

Output:输出:

------ Test started: Assembly: EFTest.dll ------

Thread Before Async/Await: 6
Thread After Async/Await: 33

1 passed, 0 failed, 0 skipped, took 4.38 seconds (NUnit 3.10.1).

Again, the code after the await is executed on another thread from the original.同样,await 之后的代码是在原来的另一个线程上执行的。

If you want to ensure that the code calling async code remains on the same thread then you need to use the Result on the Task to block the thread until the async task completes:如果要确保调用异步代码的代码保留在同一线程上,则需要使用Task上的Result来阻止线程,直到异步任务完成:

    [Test]
    public void TestAsync3()
    {
        using (var context = new TestDbContext())
        {
            Console.WriteLine("Thread Before Async: " + Thread.CurrentThread.ManagedThreadId.ToString());
            var names = context.Customers.Select(x => x.Name).ToListAsync();
            Console.WriteLine("Thread After Async: " + Thread.CurrentThread.ManagedThreadId.ToString());
            var result = names.Result;
            Console.WriteLine("Thread After Result: " + Thread.CurrentThread.ManagedThreadId.ToString());
        }
    }

Output:输出:

------ Test started: Assembly: EFTest.dll ------

Thread Before Async: 20
Thread After Async: 20
Thread After Result: 20

1 passed, 0 failed, 0 skipped, took 4.16 seconds (NUnit 3.10.1).

So as far as Unity, EF, etc. goes, you should be cautious about using async liberally where these classes are not thread safe.因此,就 Unity、EF 等而言,在这些类不是线程安全的情况下,您应该谨慎使用异步。 For instance the following code may lead to unexpected behaviour:例如,以下代码可能会导致意外行为:

        using (var context = new TestDbContext())
        {
            var ids = await context.Customers.Select(x => x.CustomerId).ToListAsync();
            foreach (var id in ids)
            {
                var orders = await context.Orders.Where(x => x.CustomerId == id).ToListAsync();
                // do stuff with orders.
            }
        }

As far as the code goes this looks fine, but DbContext is not thread-safe and the single DbContext reference will be running on a different thread when it is queried for Orders based on the await on the initial Customer load.就代码而言,这看起来不错,但 DbContext不是线程安全的,当根据初始客户负载的等待查询订单时,单个 DbContext 引用将在不同的线程上运行。

Use async when there is a significant benefit to it over synchronous calls, and you're sure the continuation will access only thread-safe code.当异步调用比同步调用有显着优势时使用异步,并且您确定延续将仅访问线程安全代码。

I'm amazed there is no mention of ConfigureAwait in this thread.我很惊讶在这个线程中没有提到 ConfigureAwait。 I was looking for a confirmation that Unity did async/await the same way that it is done for "regular" C# and from what I see above it seems to be the case.我一直在寻找一个确认,即 Unity 以与“常规”C# 相同的方式执行 async/await,从我上面看到的情况来看,情况似乎确实如此。

The thing is, by default, an awaited task will resume in the same threading context after completion.事情是,默认情况下,等待的任务将在完成后在相同的线程上下文中恢复。 If you await on the main thread, it will resume on the main thread.如果您在主线程上等待,它将在主线程上恢复。 If you await on a thread from the ThreadPool, it will use any available thread from the thread pool.如果您等待来自 ThreadPool 的线程,它将使用线程池中的任何可用线程。 You can always create different contexts for different purposes, like a DB access context and whatnot.您始终可以为不同的目的创建不同的上下文,例如数据库访问上下文等等。

This is where ConfigureAwait is interesting.这就是 ConfigureAwait 有趣的地方。 If you chain a call to ConfigureAwait(false) after your await, you are telling the runtime that you do not need to resume in the same context and therefore it will resume on a thread from the ThreadPool.如果在 await 之后链接对ConfigureAwait(false)的调用,则是在告诉运行时您不需要在同一上下文中恢复,因此它将在来自 ThreadPool 的线程上恢复。 Omitting a call to ConfigureAwait plays it safe and will resume in the same context (main thread, DB thread, ThreadPool context, whatever the context the caller was on).省略对 ConfigureAwait 的调用可以安全地播放,并将在相同的上下文(主线程、DB 线程、ThreadPool 上下文,无论调用者所在的上下文)中恢复。

So, starting on the main thread, you can await and resume in the main thread like so:因此,从主线程开始,您可以像这样在主线程中等待和恢复:

    // Main thread
    await WhateverTaskAync();
    // Main thread

or go to the thread pool like so:或者像这样转到线程池:

    // Main thread
    await WhateverTaskAsync().ConfigureAwait(false);
    // Pool thread

Likewise, starting from a thread in the pool:同样,从池中的一个线程开始:

    // Pool thread
    await WhateverTaskAsync();
    // Pool thread

is equivalent to :相当于:

    // Pool thread
    await WhateverTaskAsync().ConfigureAwait(false);
    // Pool thread

To go back to the main thread, you would use an API that transfers to the main thread:要返回主线程,您将使用传输到主线程的 API:

    // Pool thread
    await WhateverTaskAsync().ConfigureAwait(false);
    // Pool thread
    RunOnMainThread(()
    {
        // Main thread
        NextStep()
    });
    // Pool thread, possibly before NextStep() is run unless RunOnMainThread is synchronous (which it normally isn't)

This is why people say that calling Task.Run runs code on a pool thread.这就是为什么人们说调用 Task.Run 在池线程上运行代码。 The await is superfluous though...虽然等待是多余的......

   // Main Thread
   await Task.Run(()
   {
       // Pool thread
       WhateverTaskAsync()
       // Pool thread before WhateverTaskAsync completes because it is not awaited
    });
    // Main Thread before WhateverTaskAsync completes because it is not awaited

Now, calling ConfigureAwait(false) does not guarantee that the code inside the Async method is called in a separate thread.现在,调用 ConfigureAwait(false) 并不能保证在单独的线程中调用 Async 方法中的代码。 It only states that when returning from the await, you have no guarantee of being in the same threading context.它仅说明从等待返回时,您不能保证处于同一线程上下文中。

If your Async method looks like this:如果您的 Async 方法如下所示:

private async Task WhateverTaskAsync()
{
    int blahblah = 0;

    for(int i = 0; i < 100000000; ++i)
    {
        blahblah += i;
    }
}

... because there is actually no await inside the Async method, you will get a compilation warning and it will all run within the calling context. ...因为实际上 Async 方法中没有 await,您将收到编译警告,并且它将全部在调用上下文中运行。 Depending on its ConfigureAwait state, it might resume on the same or a different context.根据其 ConfigureAwait 状态,它可能会在相同或不同的上下文中恢复。 If you want the method to run on a pool thread, you would instead write the Async method as such:如果您希望该方法在池线程上运行,您可以这样编写 Async 方法:

private Task WhateverTaskAsync()
{
    return Task.Run(()
    {
        int blahblah = 0;

        for(int i = 0; i < 100000000; ++i)
        {
            blahblah += i;
        }
    }
}

Hopefully, that clears up some things for others.希望这可以为其他人清除一些事情。

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

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