【问题标题】:Execute Task in new background thread在新的后台线程中执行任务
【发布时间】:2019-05-28 12:17:09
【问题描述】:

我知道这方面有一百万个问题,我正在尝试他们的解决方案,但它的表现似乎与我预期的不同。

使用以下代码:

await Task.Run(() => { Thread.Sleep(5000); Console.WriteLine("I'm running after Thread.Sleep"); });
Console.WriteLine("I'm running after Task.Run");

我得到了输出:

[ 5 seconds elapses ]
I'm running after Thread.Sleep
I'm running after Task.Run

如果不使用Task.Run,我会得到相同的输出

我想要的是输出:

I'm running after Task.Run
[ 5 seconds elapses ]
I'm running after Thread.Sleep

“父”线程继续执行,长时间运行的任务在“后台”以不影响“父”线程的方式执行。

【问题讨论】:

  • 您需要从task.run中删除await关键字,因为您正在等待Task.Run的执行以便继续执行您的程序

标签: c# multithreading asynchronous


【解决方案1】:

如上所述,您需要删除 await然而这会将其变成即发即弃任务,如果上面的代码恰好位于控制台应用程序的Main 中,那么您的进程可能会在之前终止任务已完成。

好:

 // C# 7
 static async void Main(string[] args) // <--- note `async` signature
 {
     await Task.Run(() => { Thread.Sleep(5000); Console.WriteLine("I'm running 
                 after Thread.Sleep"); });
     Console.WriteLine("I'm running after Task.Run");
 }

不好: - 不是awaited 的即发即弃任务

 static void Main(string[] args)
 {
     Task.Run(() => { Thread.Sleep(5000); Console.WriteLine("Spider Man: I don't wan't to go!"); });  // POOF!! 

     Console.WriteLine("I'm running after Task.Run");

     // sorry Spidy, you're dead
 }

因此,即使您有Thread.Sleep(),您也可能根本看不到I'm running after Thread.Sleep。 (顺便说一句,您应该使用Task.Delay())。您可以使用 Console.ReadKey() 缓解它,但这可能会破坏目的。

即发即弃任务有其用途,并且可以在其他类型的长期运行应用程序中合理安全地使用,例如 WinForms,但人们通常不会在进程条目中生成任务点,因此任务通常会运行到完成,而不会被进程提前终止。但是,在可能的情况下使用await 通常是个好主意,这样您就不会伤到自己的脚。

此外,您以某种顺序想要事物的要求表明您对 async/await 的工作原理存在误解。

【讨论】:

  • Task.Run 让我失望了,因为我一直都知道你必须通过方法返回 awaitWait() Task,否则什么都不会发生。但是Task.Run 是该规则的一个例外:它返回一个Task lambda 函数运行而不执行await Task.Run
  • @Frayt “否则什么都不会发生” - 不正确。调用async 方法返回hot task, a task that has already been started。通过添加await,您可以确保Task 有机会在执行代码的下一行之前运行完成。 Task.Run() 是一个“方法” 也是returns a hot task。使用Wait() 作为最后的手段。如果您从本身可以更改为async 的方法调用async 方法,则可以避免使用Wait()Wait 达不到目的
【解决方案2】:

使用await 时要记住的一件事是您的代码等待对象。这些对象就像任何其他对象一样。具体来说,它们可以分配给局部变量或类变量。

所以,这段代码:

await Task.Run(() => { Thread.Sleep(5000); Console.WriteLine("I'm running after Thread.Sleep"); });
Console.WriteLine("I'm running after Task.Run");

与这段代码基本相同:

var task = Task.Run(() => { Thread.Sleep(5000); Console.WriteLine("I'm running after Thread.Sleep"); });
await task;
Console.WriteLine("I'm running after Task.Run");

换句话说:

  1. 启动在后台线程上运行的任务。
  2. 异步等待 (await) 该任务完成。
  3. 显示"I'm running after Task.Run"

所以输出是预期的。

我想要的是输出:

我在 Task.Run 之后运行

[ 5 秒过去了 ]

我在 Thread.Sleep 之后运行

在这种情况下,启动在后台线程上运行的任务,但稍后再await它:

var task = Task.Run(() => { Thread.Sleep(5000); Console.WriteLine("I'm running after Thread.Sleep"); });
Console.WriteLine("I'm running after Task.Run");
await task;

【讨论】:

    【解决方案3】:

    如果你想异步运行,你应该删除命令

            static void Main(string[] args)
            {
                Task.Run(() => { Thread.Sleep(5000); Console.WriteLine(DateTime.Now + " => I'm running after Thread.Sleep"); });
                Console.WriteLine(DateTime.Now + " => I'm running after Task.Run");
                while (true)
                {
                    Thread.Sleep(500);
                }
            }
    

    输出 28/05/2019 09:49:58 => I'm running after Task.Run 28/05/2019 09:50:03 => I'm running after Thread.Sleep

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-06-17
      • 2019-08-18
      • 1970-01-01
      • 1970-01-01
      • 2013-06-14
      • 1970-01-01
      • 1970-01-01
      • 2012-02-28
      相关资源
      最近更新 更多