【问题标题】:Simulate async/await with task.continuewith in .net 4.0在 .net 4.0 中使用 task.continuewith 模拟 async/await
【发布时间】:2015-02-24 18:17:51
【问题描述】:

是否可以在不使用Microsoft.Bcl.Async 包的情况下模拟 .NET 4.0 中的 async/await 的行为?

我试着跑了

Task myTask = Task.Factory.Startnew(MyMethod,...)
myTask.ContinueWith((x)=>{
    //do stuff when myTask is completed
},...);
//code that i hoped would run without waiting for myTask

但这会在 myTask 运行时阻止我的 UI。在搜索这个问题时,我发现this question 似乎问的是完全相同的问题,甚至提供了一个带有完整代码示例的解决方案。但是,当我尝试调用GetResponseWithRetryAsync(或使用task.Start() 运行它,或将其包装在Task.Factory.StartNew() 中)时,它仍然会阻塞我的UI 线程。为什么所有这些运行任务的方式都会阻塞我的 UI?

编辑:根据用户 1 的请求阻止我的 UI 的示例代码

Task myTask = Task.Factory.StartNew(MyMethod, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());
myTask.ContinueWith((x) =>
    { 
     this.Title="Done!";
    }, new CancellationToken(), TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.FromCurrentSynchronizationContext());

MyMethod 在哪里

    public void MyMethod(){
        WebRequest request = WebRequest.Create("http://google.com/");
        request.Credentials = CredentialCache.DefaultCredentials;
        WebResponse response = request.GetResponse();
        Stream dataStream = response.GetResponseStream();
        StreamReader reader = new StreamReader(dataStream);

        string responseFromServer = reader.ReadToEnd();
        Console.WriteLine(responseFromServer);
        reader.Close();
        response.Close();
    }

【问题讨论】:

  • 如果您发布一个特定的最小示例,我们可以准确地告诉您它是否阻塞。否则只是猜测。
  • 你能告诉我们这个方法执行的完整上下文吗?您是否将任何TaskScheduler 传递给它?我们需要更多代码。
  • 就像你在我的编辑中看到的那样,我使用TaskScheduler.FromCurrentSynchronizationContext()。我认为这是默认设置 - 这是否意味着我的执行任务也在当前同步上下文中运行?!
  • 使用 TaskScheduler.Default 不会阻止我的用户界面...我想这毕竟是一个愚蠢的问题 ^^ 谢谢你的帮助!
  • @HW 从你的任务中取出, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());。您正在通过将代码放在那里来在当前 UI 线程上运行您的任务。因此您阻止了 UI

标签: c# asynchronous .net-4.0 async-await


【解决方案1】:

可以在没有编译器和框架帮助的情况下模仿async-await,尽管很难做到正确。您需要注意捕获上下文、异常处理、返回结果等等。

您提到的情况不应该在整个异步操作过程中阻塞 UI。但是,在 DNS 解析等异步操作之前,可能会进行一些同步工作。如果是这种情况,请使用 Task.Run 并将该工作卸载到 ThreadPool 线程以提高响应能力。

【讨论】:

  • 这听起来像你在说Task.Run 已经异步启动了一个任务并且不会阻塞我的 UI 线程?从我迄今为止尝试和阅读的内容来看,这不是真的?
  • @HW Task.Run 将 CPU 绑定的工作卸载到另一个线程。 async 方法在其异步部分中不需要线程。 Task.Run 不会阻止。如果你有一个案例,那么你就有一个错误。
  • @HW 如果您使用WaitResult 同步等待返回的任务,那么您将阻塞UI线程。
  • 你是对的.. 我用Task.Run(()=>{GetResponseWithRetryAsync(url, 5);}); 尝试了这个,它实际上在没有阻塞我的 UI 的情况下运行(虽然这看起来有点复杂,我怎样才能将我的方法转换为 action 并传递方法Task.Run 马上?)。因为即使你说模拟 async-await 也很难做到正确,我现在会尽量使用给定的工具,以便首先更好地理解它们的行为。我希望这会很容易(从我最初的方法中可以看出)。
  • @HW 您的意思是要将同步的MyMethod 传递给Task.Run?您可以像使用 StartNew 一样执行此操作。 Task.Run 只使用线程池而不指定TaskScheduler.Default
【解决方案2】:
Task myTask = Task.Factory.Startnew(MyMethod)
myTask.ContinueWith((x)=>
{
    //do stuff when myTask is completed
},new CancellationToken(),TaskContinuationOptions.OnlyOnRanToCompletion,TaskScheduler.FromCurrentSynchronizationContext());

这意味着 continue with 不会阻塞你的 UI 线程。这里主要是TaskScheduler.FromCurrentSynchronisationContext()

【讨论】:

  • 显然OnlyOnCompleted 不是TaskContinuationOptions 的成员?你的意思是OnlyOnRanToCompletion? (尽管使用 OnlyOnRanToCompletion 的建议设置仍会阻止我的 UI 线程)
  • @HW 是的,我的意思是OnlyOnRanToCompletion 我的错误。它不应该阻塞 UI 线程
  • @HW 阅读您的一些 cmets 似乎表明您的错误在 MyMethod 中。 MyMethod 中的某些东西阻塞了 UI 线程,例如如果您要在该线程中更新 UI 的任何元素。我的示例不会阻塞 UI 线程。我认为如果你用你试图在任务中运行的内容更新你的问题会有所帮助
  • 我用阅读您的答案后尝试的代码更新了我的问题。
猜你喜欢
  • 2017-03-28
  • 2014-02-16
  • 1970-01-01
  • 1970-01-01
  • 2015-10-26
  • 2012-02-24
  • 2012-11-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多