跳过异步/等待?

发布于 2025-02-07 05:27:55 字数 968 浏览 2 评论 0原文

我试图了解在某些情况下跳过异步/等待异步或是否会导致陷阱的优化。下面的代码是一个示例,但有助于简化我的问题和理解。

说我有以下代码:

public async Task<string> CreateRandomString()
{
    var myTitleTask = GetTitle();

    var randomString = CreateRandomString();

    var myTitle = await myTitleTask;
    
    return myTitle + randomString;
}

private async Task<string> GetTitle()
{
    return await GetTitleFromWebServiceAsync();
}

可以从getTitle()这样的方法中删除async/等待

public async Task<string> CreateRandomString()
{
    var myTitleTask = GetTitle();

    var randomString = CreateRandomString();

    var myTitle = await myTitleTask;
    
    return myTitle + randomString;
}

private Task<string> GetTitle()
{
    return GetTitleFromWebServiceAsync();
}

例如我们将尽可能长时间地延迟到任务getTitle(),从而进行其他工作直到我们等待,还是会引起任何问题?

在第二个示例中,我认为这是更优化和更好的方法,但只想确保我不会陷入任何陷阱。请对此做出想法或评论?

I'm trying to understand if there's an optimisation in skipping async/await in certain situations or whether this could lead to a pitfall. Code below is made up example but it'll help to simplify for my question and understanding.

Say I have the following code:

public async Task<string> CreateRandomString()
{
    var myTitleTask = GetTitle();

    var randomString = CreateRandomString();

    var myTitle = await myTitleTask;
    
    return myTitle + randomString;
}

private async Task<string> GetTitle()
{
    return await GetTitleFromWebServiceAsync();
}

It's possible to remove the async/await from the GetTitle() method like this:

public async Task<string> CreateRandomString()
{
    var myTitleTask = GetTitle();

    var randomString = CreateRandomString();

    var myTitle = await myTitleTask;
    
    return myTitle + randomString;
}

private Task<string> GetTitle()
{
    return GetTitleFromWebServiceAsync();
}

Does this optimise anything because we are delaying as long as possible to await the Task from GetTitle() and thus doing other work until we await or does this cause any problems?

In the second example I thought this was more optimise and better approach but just want to make sure I'm not falling into any pitfall. Thoughts or comments on this please?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

饭团 2025-02-14 05:27:55

这样可以优化任何事情,因为我们延迟了很长时间
尽可能等待GetTitle()等待任务,从而进行其他工作,直到我们等待或这会引起任何问题?

好吧,我不会进入基准测试,但这是我有时会做的。如果我需要“扫描”图像或某些文件的文件夹。我以目录循环的范围启动任务,然后通过它们枚举以获取相关文件,退出循环并等待所有文件,即并行浏览每个文件夹。而不是task t = task.run(()=&gt; foo());我们始终可以按照您的建议做,只需返回任务而不是等待它,然后在末尾>等待myTask等待mytasks

也有其他用途,例如MicroServices Architecture,您可能想“ fire and Insplo of of fire and忘记”创建的订单,例如电子邮件服务,通知服务等...处理所有这些,然后将成功返回给用户。就像您在亚马逊上下订单一样,您只能感谢您下订单。对于像您这样的顺序函数,我看不到在第三行或第五行上等待它的好处。这确实取决于代码。

Does this optimise anything because we are delaying as long
as possible to await the Task from GetTitle() and thus doing other work until we await or does this cause any problems?

Well, I'm not going to go into benchmarks but this is what I sometimes do. If I need to "scan" a folder for images or some files. I start tasks in a foreach loop of directories and enumerate through them to get the relevant files, exiting the loop and await them all i.e. look through each folder in parallel. Instead of Task t = Task.Run(() => foo()); we can always do as you suggested, simply return the task instead of awaiting it and then in the end await myTask or await myTasks

There are also other uses like in a microservices architecture, you might want to "fire and forget" about an order created, such that email service, notification service, etc... handle all that and you return success to the user. Pretty much just like you place an order on Amazon and you only get a thank you for placing the order. For a sequential function like yours, I don't see a benefit of awaiting it on the 3rd line or the 5th line. It really depends on the code.

秋意浓 2025-02-14 05:27:55

我试图了解在某些情况下跳过异步/等待异步或是否会导致陷阱的优化。

当您用一种方法进行一次调用时,可以肯定地省略异步+等待。

Task Method1Async()
  => Method2Async(); // Super safe

当方法中有多个语句时,事情可能会出错。

来自Stephen Cleary的 nofollow noreferrer“

在浸出异步和等待的最常见错误之一是,开发人员忘记了他们方法末尾需要在适当时间运行的代码。特别是,使用使用语句:

  public async任务&lt; string&gt; getWithKeyWordsAsync(字符串URL)
{
   使用(var client = new httpclient())
       返回等待client.getstringasync(url);
}

公共任务&lt; string&gt; getElidingKeywordSasync(字符串URL)
{
   使用(var client = new httpclient())
       返回client.getstringasync(url);
}
 

在此示例中,ELIDID关键字将中止下载。

本文还显示了当涉及例外和更细微的异步案例时的问题,并且例外部件与您的示例有关(因为您执行任务t = ...;其他代码;其他代码;等待T;) :

  public async任务&lt; string&gt; getWithKeywordSasync()
{
    字符串url = / *可以抛出异常的东西 * /;
    返回等待下载stringasync(url);
}

公共任务&lt; string&gt; getElidingKeywordSasync()
{
    字符串url = / *可以抛出异常的东西 * /;
    返回downloadStringAsync(url);
}
 

(...)

  var task = getWithKeywordSasync();
变量结果=等待任务; //在这里抛出异常

var task = getElidingKeywordSasync(); //在这里抛出异常
变量结果=等待任务;
 

I'm trying to understand if there's an optimisation in skipping async/await in certain situations or whether this could lead to a pitfall.

When you do a single call in a method it's safe to omit async+await.

Task Method1Async()
  => Method2Async(); // Super safe

When there are multiple statements in the method things can go wrong.

From Stephen Cleary's Eliding Async and Await:

One of the most common mistakes in eliding async and await is that developers forget that there is code at the end of their method that needs to run at the appropriate time. In particular, when using a using statement:

public async Task<string> GetWithKeywordsAsync(string url)
{
   using (var client = new HttpClient())
       return await client.GetStringAsync(url);
}

public Task<string> GetElidingKeywordsAsync(string url)
{
   using (var client = new HttpClient())
       return client.GetStringAsync(url);
}

In this example, eliding the keywords will abort the download.

The article also shows issues when exceptions are involved and a more nuanced case of AsyncLocal and the exceptions part is relevant to your example (because you do Task t = ... ; other code; await t;):

public async Task<string> GetWithKeywordsAsync()
{
    string url = /* Something that can throw an exception */;
    return await DownloadStringAsync(url);
}

public Task<string> GetElidingKeywordsAsync()
{
    string url = /* Something that can throw an exception */;
    return DownloadStringAsync(url);
}

(...)

var task = GetWithKeywordsAsync();
var result = await task; // Exception thrown here

var task = GetElidingKeywordsAsync(); // Exception thrown here
var result = await task;
诗笺 2025-02-14 05:27:55

可以从类似的getTitle()方法中删除异步/等待:

是的,这样做是完全安全的。

这是否优化了任何东西,因为我们延迟了尽可能长的时间来等待getTitle()的任务,从而进行其他工作直到我们等待任何问题?

它们是直接等效的,跳过异步/等待的效率应该更高,但我希望差异很小。如果您对实际数字感兴趣,我建议您进行一些基准测试。

It's possible to remove the async/await from the GetTitle() method like this:

Yes, and it is perfectly safe to do so.

Does this optimise anything because we are delaying as long as possible to await the Task from GetTitle() and thus doing other work until we await or does this cause any problems?

They are directly equivalent, skipping a async/await should be slightly more efficient, but I would expect the difference to be minimal. I would recommend doing some benchmarking if you are interested in actual numbers.

↘人皮目录ツ 2025-02-14 05:27:55

是的,当您发现自己仅在返回语句中使用等待时,从方法签名中删除异步关键字会优化性能和捆绑尺寸。
C#编译器将用异步标记的每种方法转换为继承IASYNCSTATEMACHINE的单独类!
它基本上将异步方法分为几个同步部分(2个等待= 3个部分),并根据类状态执行每个部分。
因此,将方法修改为类后,将100个字节的开销添加到您的最终捆绑包大小(如今通常是一个很小的问题)。
而且,当存在等待关键字时,运行时的性能开销必须切换线程。因此,返回任务而无需等待任务将导致上下文切换较少,这非常昂贵。

Yes, when you find yourself only using await in the return statement, removing the async keyword from the method signature optimizes performance and bundle size.
The C# compiler turns each method marked with async to a separate class that inherits IAsyncStateMachine!
It basically divides the async method into several synchronous portions(2 awaits = 3 portions) and executes each portion depending on the class state.
Thus, having modified the method to a class, an overhead of 100 bytes is added to your final bundle size(usually a very minor problem nowadays).
And there is also the performance overhead of the runtime having to switch threads when the await keyword is present. So returning the task without awaiting it when possible will result in less context-switching, which is quite expensive.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文