当前位置: 首页 > 知识库问答 >
问题:

为什么当我试图在另一个线程上运行异步方法时会得到警告?

冷正信
2023-03-14

我有一个异步方法,它调用另一个异步方法,但是,我希望它在单独的线程上并行运行:

public async Task<Page> ServePage() {
  Task.Run(() => DoThings(10));   // warning here

  // ... other code
  return new Page();
}

public async Task DoThings(int foo) {
  // stuff
}

事实上,这就是我正在努力做的。为什么我会收到编译器警告?task.run的语法不正确吗?

共有1个答案

袁飞鹏
2023-03-14

DR

你得到警告的原因是因为

 Task.Run(() => DoThings(10));   // warning here

返回一个任务,由于servePage方法被标记为异步,编译器认为您应该等待任务的结果

    null
public Page ServePage() // If we are CPU bound, there's no point decorating this as async
{
    var taskX = Task.Run(() => CalculateMeaningOfLife()); // Start taskX
    var taskY = Task.Run(() => CalculateJonSkeetsIQ()); // Start taskY
    var z = DoMoreHeavyLiftingOnCurrentThread();
    Task.WaitAll(taskX, taskY); // Wait for X and Y - the Task equivalent of `Thread.Join`

    // Return a final object comprising data from the work done on all three tasks
    return new Page(taskX.Result, taskY.Result, z);
}

这与async/await相反,后者通常用于在等待I/O绑定调用完成时释放线程。假设doThings确实是I/O绑定的,看起来类似

public async Task<string> DoThings(int foo) {
   var result = await SomeAsyncIo(foo);
   return "done!";
}

您可以并行但仍然异步地执行:

public async Task<Page> ServePage() {
   var task1 = DoThings(123); // Kick off Task 1
   var task2 = DoThings(234); // Kick off Task 2 in parallel with task 1
   await Task.WhenAll(task1, task2); // Wait for both tasks to finish, while releasing this thread
   return new Page(task1.Result, task2.Result); // Return a result with data from both tasks
}

如果I/O绑定工作花费了合理的时间,那么在Await任务中很有可能出现一个点。当所有实际运行零个线程时-请参见Stephen Cleary的文章。

public Page ServePage() // No async
{
  #pragma warning disable 4014 //   warning is suppresed by the Pragma
  DoThings(10);   // Kick off DoThings but don't wait for it to complete.
  #pragma warning enable 4014

  // ... other code
  return new Page();
}

RE:

这其中的细微差别让我一遍又一遍地逃避,比如调用从同步方法返回任务的异步方法会激发并忘记该方法的执行。(这是最后一个代码示例。)我理解得对吗?

这有点难解释,但是不管是使用还是不使用await关键字调用,在第一个await之前调用的Async方法中的任何同步代码都将在调用者的html" target="_blank">线程上执行,除非我们使用像task.run这样的锤子。

这个示例可能有助于理解(注意,我们故意使用同步thread.sleep而不是await task.delay来模拟CPU绑定的工作,并引入可以观察到的延迟)

public async Task<Page> ServePage()
{
  // Launched from this same thread, 
  // returns after ~2 seconds (i.e. hits both sleeps) 
  // continuation printed.
  await DoThings(10);

  #pragma warning disable 4014
  // Launched from this same thread, 
  // returns after ~1 second (i.e. hits first sleep only) 
  // continuation not yet printed
  DoThings(10);   

  // Task likely to be scheduled on a second thread
  // will return within few milliseconds (i.e. not blocked by any sleeps)
  Task.Run(() => DoThings(10));   

  // Task likely to be scheduled on a second thread
  // will return after 2 seconds, although caller's thread will be released during the await
  // Generally a waste of a thread unless also doing CPU bound work on current thread, or unless we want to release the calling thread.
  await Task.Run(() => DoThings());   

  // Redundant state machine, returns after 2 seconds
  // see return Task vs async return await Task https://stackoverflow.com/questions/19098143
  await Task.Run(async () => await DoThings());   
}

public async Task<string> DoThings(int foo) {
   Thread.Sleep(1000); 
   var result = await SomeAsyncIo(foo);
   Trace.WriteLine("Continuation!"); 
   Thread.Sleep(1000); 
   return "done!";
}

还有一个要点需要注意--在大多数情况下,不能保证await之后的继续代码将在与await之前相同的线程上执行。编译器将延续代码重写到一个任务中,延续任务将在线程池上进行调度。

 类似资料:
  • 在我的应用程序中,我有一个队列,其中包含数据库中应该发送到服务器的项目。由于我希望这个队列被持续监视,并且由于这个任务包括网络通信,所以我创建了一个实现Runnable的类,我将其称为QueueWorkerThread。但是出于某种原因,我仍然得到了“Android.os.NetworkonMainThreadException”,我不明白为什么?我尝试在SO上搜索类似的问题,但任何更接近的都是使

  • 然而,在我下面的代码中,我希望在这两个示例中都花费相同的15秒(每个任务5秒),如本文所述。然而,第二个示例只需要5秒,同时运行所有3个示例也需要5秒来完成第二个示例。原来的文章花了5秒,但我把它改成了1秒的延迟,让它更明显。 有没有人能解释一下这是怎么回事,为什么它看起来像线程一样运行?

  • 问题内容: 我在同一文件中有以下程序。我已经同步了run()方法。 输出是 我的问题是,为什么同步方法同时允许“我的线程1”和“我的线程4”线程访问? 问题答案: 方法在实例级别工作。 类的每个实例都有自己的锁。每次输入实例的任何方法都将获取该锁。这样可以防止多个线程 在同一个实例上 调用方法(请注意,这还可以防止在同一个实例上调用 不同的 方法)。 现在,由于您有两个类实例,因此每个实例都有自己

  • 我试图在Eclipse中调试一个简单的java程序,结果出现了以下错误: 本机方法中的致命错误:JDWP未初始化任何传输,jvmtiError=AGENT_ERROR_TRANSPORT_INIT(197)错误:传输错误202:连接失败:连接超时错误:JDWPTransport dt_socket未能初始化,TRANSPORT_nit(510)JDWP退出错误AGENT_ERROR_TRANSPO

  • 我遇到了JavaFX ScatterChart组件的一个令人讨厌的错误。我想创建一个图表,不断得到更多的点添加到它。为此,我首先创建一个新系列,将其添加到图表中,然后开始向该系列添加点。 这在Linechart中工作得很好。然而,由于某种原因,当我试图对散点图执行同样的操作时,会得到。 下面是一个针对这种情况的独立测试用例: 以下是例外情况的相关部分: 有人知道为什么会这样吗?

  • 我还尝试了,,JMeter获得了多达8000个示例,最大时间12000ms(超时30s),并给出了错误。在拒绝之前,它至少应该排队10,000个连接。