【问题标题】:Why is delaying the task not executing it fully or blocking the UI?为什么延迟任务不能完全执行或阻塞 UI?
【发布时间】:2017-12-05 11:21:18
【问题描述】:
private async Task<string> httpClient(CancellationToken cancelToken)
{
    HttpClient hc = new HttpClient();

    hc.Timeout = new TimeSpan(0, 0, 10);

    //Task.Delay(5000).Wait(); using this one, it blocks the UI thread
    //await Task.Delay(5000); using this one, it doesn't execute the task after delay

    if (cancelToken.IsCancellationRequested)
    {
        return null;
    }

    HttpResponseMessage response = await hc.GetAsync(new Uri("http://google.com/"));

    response.EnsureSuccessStatusCode();

    string responseData = await response.Content.ReadAsStringAsync();

    return responseData;
}

这是我的异步任务,我在尝试在其中使用延迟时遇到问题。我尝试了两种方法,似乎都导致任务出现问题。尝试研究但找不到解决我的问题的方法。任何帮助表示赞赏

其他部分代码:

private async void Test()
{
    string response = await httpClient(token);
    Console.WriteLine("response: " + response);
}

private void button1_Click(object sender, EventArgs e)
{
    Task t = new Task(Test);
    t.Start();
    Console.WriteLine("task started");
    t.Wait();
    Console.WriteLine("task finished");
}

【问题讨论】:

  • Task.Delay(5000).Wait(); - 是的,这会阻塞 UI 线程(如果它完成的话);不要那样做;至于await Task.Delay(5000); - 看起来应该可以工作 - 实际发生了什么?
  • 取消令牌是否超时?
  • @MarcGravell 我添加了代码的另一部分。当我单击 button1 时会发生什么情况,它会立即打印出“任务开始”和“任务完成”。
  • 不要为每个请求创建一个新的 HttpClient,为所有请求保留一个对象(参考此处aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong)。此外,您应该将 cancelToken 传递给您等待的方法,将其传递给 GetAsync。
  • @gafs 这就是为什么我们不使用async void :)

标签: c# multithreading asynchronous async-await task


【解决方案1】:

问题出在这里:

private async void Test()
{
    string response = await httpClient(token);
    Console.WriteLine("response: " + response);
}

一旦你做了一些事情async void,你就完全取消了跟踪状态的能力。您的new Task(Test); 正在使用new Task(Action),它将在代码首次返回给调用者时立即报告完成 - 即在第一个不完整的await(在您的情况下:Task.Delay)。做你想做的事,你真的应该使用Task.Run(Func&lt;Task&gt;) API(或者完全避免Task.Run/Task.Start,依赖异步管道本身),使用private async Task Test()方法。

您的事件处理程序可能是:

private async void button1_Click(object sender, EventArgs e)
{
    Console.WriteLine("about to start task");
    var t = Task.Run(Test);
    Console.WriteLine("task started");
    await t;
    Console.WriteLine("task finished");
}

或避免额外的线程(如 Nkosi 所述):

private async void button1_Click(object sender, EventArgs e)
{
    Console.WriteLine("about to start task");
    var t = Test();
    Console.WriteLine("task started");
    await t;
    Console.WriteLine("task finished");
}

【讨论】:

  • Task.Run 可以一起删除,只删除await Test()
  • @Nkosi 是的,这也是真的
  • 感谢您的帮助。我有一个问题。如果我要将 void 更改为 Task&lt;string&gt; 和 return response,我将如何从我的按钮调用任务?我使用了Task t = new Task(() =&gt; Test()); 并得到了一个warning 说Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.。这有什么好担心的吗?
  • @gafs var response = await Test() 提供private Task&lt;string&gt; Test() { ... return response;}
  • @gafs 你没有await t;,然后......请注意,我只包含了t 本地,以允许您在开始和完成之间登录 - 更惯用的用法是简单地说:await Test();。如果您将其更改为Task&lt;string&gt;,那么它将是var result = await Test();
猜你喜欢
  • 2020-02-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-20
  • 2019-08-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多