【问题标题】:Does threads work with an await statement?线程是否与 await 语句一起使用?
【发布时间】:2020-08-08 09:26:44
【问题描述】:

这是在 C# 中。

我有一个名为 Login 的异步函数,它是一个 await 语句,用于等待登录到 api。 这工作正常。所以我做到了,这个异步函数是从另一个线程调用的,因为我一次有多个登录。我首先在没有 await 语句的情况下测试线程,它在函数中运行良好。现在我尝试使用 await 语句进行真正的登录,而我的线程只是在 await 语句处停止并且不再执行任何操作。

        public async void Login()
        {

            var loginRequest = await api.LoginAsync();

            if (loginRequest.Succeeded)
            {
                status = "LOGGED_IN";
                message = "logged in";
            } else
            {
                status = "ERROR";
                message = loginRequest.Info.Message;
            }
            Brain.BotRespond(ID);
        }

【问题讨论】:

  • 您应该避免使用async void 函数并使用async Taskasync Task<T>
  • 混合线程和任务会带来麻烦。使用一个或另一个,而不是同时使用,除非框架让你。
  • 贴出调用这个的代码。
  • 还有LoginAsync的代码。

标签: c# multithreading async-await


【解决方案1】:

您同时有多个登录不是将其放在单独线程中的理由。

对我理解 async-await 有很大帮助的一件事是 this interview with Eric Lippert。在中间的某处搜索异步等待。

他将 async-await 与必须做几份早餐的厨师进行比较。厨师打开水壶煮茶。他没有等到水沸腾了。相反,他环顾四周,看看是否可以做其他事情:他可以将面包放入烤面包机,或者从第二份早餐开始。

请注意,厨师必须等待的唯一时刻是另一个进程(例如水壶或面包烤面包机)正在做某事时:如果厨师空闲等待或不等待,则 if 不会加速该过程。

您会在计算机中看到类似的情况:您会发现 async await 仅在涉及另一个自治进程时。就像当你想将数据写入磁盘,或者从数据库管理系统查询数据,或者从互联网上读取信息时:你的进程不能做任何事情来加速这个进程,而另一个进程正在做它的事情,你的进程可以做其他事情,比如听新的操作员输入。

如果厨师还要做一些繁重的加工,比如切西红柿,他会忙着处理热吐司。在这种情况下,最好聘请第二位厨师来切西红柿。水开后烤面包,厨师要等第二位厨师切完后才能继续做早餐。

您的问题是一样的:您要求操作员或进程登录。您可以做其他事情,而不是无所事事地等待登录完成,例如开始下一次登录,当该登录等待完成时,您可以开始第三次登录:全部由同一个线程完成。

只有当你必须做一些冗长的繁重处理,而不是无所事事地等待时,让一个单独的线程来做这些繁重的计算可能是有用的。只有这样做,才能让您的来电者保持响应。如果在你忙着切西红柿的时候打电话的人都没有做任何有用的事情,那就不要为此雇一个额外的厨师,因为这会让你闲着,结果就是吃完早餐需要更长的时间。

顺便说一句:每个 Async 方法都返回 Task<TResult> 而不是 TResultTask 而不是 void。此规则只有一个例外:事件处理程序返回async void,没有人可以等待事件处理程序完成。

所以你的 api 有一个 LoginAsync,你想创建一个函数来使用这个 Login。也许您还有其他一些登录名,并且您想全部调用它们,而无需等待(好吧,如果没有人完成登录,您将无事可做)

  • 异步任务 LoginAsync() // = 来自您的 API 的登录

Login 方法,让我们把它做成一个同步和异步版本,这样你就会明白为什么将登录操作与登录处理分开是明智的:

void Login()
{
    LoginResult loginResult = api.Login();
    this.ProcessLoginResult(loginResult);
}
async Task LoginAsync()
{
    LoginResult loginResult = await api.Login();
    this.ProcessLoginResult(loginResult);
    // as this is not a lengthy process, there is no async method
}

您在谈论多个登录,因此假设您有一个启动多个登录的过程。显然你的登录方法不需要通知他们的调用者登录结果,你只需要知道它们是否都完成了:

async Task PerformMultipleLoginsAsync()
{
    // start Login 1, do not await yet
    Task taskLogin1 = this.LoginAsync();

    // because you did not await, you are free to do something else
    // while LoginAsync is awaiting
    this.DoSomething();

    // start a second Login. The first might be finished, but you don't care about
    // that right now
    Task taskLogin2 = this.LoginAsync();
    this.DoSomethingElse();

    // now you need the 1st login to finish
    await taskLogin1;

    // or if you need both logins to be finished
    await Task.WhenAll(new Task[] {taskLogin1, taskLogin2});

    // of if you need that at least one login is finished:
    await Task.WhenAny(new Task[] {taskLogin1, taskLogin2});
}

如果您登录多个进程并希望在任何进程完成后立即执行某项操作,那么等待任何登录会很好:

HashSet<Task<LoginResult>> loginTasks = new HashSet(this.StartManyLogins());

while (loginTasks.Any())
{
    Task<LoginResult> finishedTask = await Tasks.WhenAny(loginTasks);
    LoginResult loginResult = finishedTask.Result;
    this.ProcessLoginResult(loginResult);

    loginTasks.Remove(finishedTask);
}

【讨论】:

  • 哇,非常感谢您的详细和非常好的回答。我现在更好地理解它,我会像你展示的那样尝试它。非常感谢你:)
猜你喜欢
  • 1970-01-01
  • 2012-09-26
  • 2012-07-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-26
  • 1970-01-01
  • 2010-12-30
相关资源
最近更新 更多