在Task.Run中的Thread.Sleep

5

最近我一直在试图理解C#的新异步/等待和Task.Run功能。为此,我编写了一些简单的测试代码,将结果写入控制台,以便我可以看到事情发生的顺序,并在这里和那里加入Thread.Sleep来确保事情按照我的预期顺序真正发生。

以下是我的其中一个测试:

[Test]
public void TaskRun()
{
    Console.WriteLine("Before");
    Task.Run(() => Console.WriteLine(_terminator.IWillBeBack()));
    Console.WriteLine("After");
}

使用以下终止器类:

public class Terminator
{
    public string IWillBeBack()
    {
        return "I will be back";
    }
}

我预计的结果是BeforeAfter,接着是I will be back。实际上也正是如此。然后我对IWillBeBack方法进行了更改,使其可以休眠3秒钟。代码如下:

public class Terminator
{
    public string IWillBeBack()
    {
        Thread.Sleep(3000);
        return "I will be back";
    }
}

我本来期望的结果应该是先显示Before, 然后是After,最后在3秒后再次显示I will be back。但事实并非如此。实际上,我只看到了BeforeAfter,却从未看到过I will be back

这是为什么呢?

当我进行调试时,代码显然已经执行完毕并返回。在任何生产代码中,我都不需要像这样使用Thread.Sleep,但我想大概了解为什么第二种情况下没有将I will be back写入控制台。


今天我有点懒,所以我只会引用答案。我几天前写了一些关于TPL+async/await的东西,这可能有助于理解:http://blog.tedd.no/2012/07/28/asyncawait-http-server-in-c/ - Tedd Hansen
2个回答

8
您的测试在尝试打印“我会回来”之前已经完成。您没有做任何事情来等待它完成。但是,根据测试运行程序的不同,它重定向Console.WriteLine的方式可能会在测试完成后立即断开连接。如果您想看到它完成,请将您的测试更改为:
[Test]
public void TaskRun()
{
    Console.WriteLine("Before");
    var task = Task.Run(() => Console.WriteLine(_terminator.IWillBeBack()));
    Console.WriteLine("After");
    task.Wait();
}

那很有道理。我运行了你的测试,现在它按预期工作 :) - Halvard
我只是“幸运”的吗?因为在第一种情况下它写入“我会回来”(因为我也没有做任何等待的操作)?在这里的“幸运”指的是,在测试完成之前,它恰好能够及时将内容写入控制台。 - Halvard
@Halvard:是的。测试拆除可能有一些“稍微昂贵”的工作要做,这可能会给你的其他线程带来优势。 - Jon Skeet
2
@Halvard:请注意,您仅应在示例控制台应用程序中使用Wait。通常情况下,不要在生产系统中混合使用阻塞式和异步式代码。您可能需要阅读我的 “async”入门指南关于async控制台程序的后续文章 - Stephen Cleary

1
如果您在VS 2012中使用MS Test框架,您也可以像这样编写测试。
[TestMethod]
public async Task TaskRun()
{
    Console.WriteLine("Before");
    Task t = Task.Run(() => Console.WriteLine(IWillBeBack()));
    Console.WriteLine("After");
    await t;
}

private string IWillBeBack()
{
    Thread.Sleep(3000);
    return "I will be back";
}

当我看到你的代码时,我期望它能够工作,但是在运行你的测试时,我得到了BeforeAfter,但没有I will be back。据我所知,task.Wait()和await task之间的区别在于前者会阻塞,而后者不会。这可能是你的代码无法正常工作的原因吗?(顺便说一句,我在VS 2012的默认测试GUI窗口中看到输出,并使用NUnit框架。这可能是我被欺骗了吗?) - Halvard
据我所知,NUnit不支持异步单元测试。我认为目前只有MSTest和xUnit支持。 - Stephen Cleary
1
更新:在之前的评论中,NUnit不支持“async”单元测试,但现在已经支持了。 - Halvard

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接