为什么将单独的方法调用视为同一任务?

时间:2018-07-11 02:55:05

标签: c# async-await

在调试问题时,我发现短期运行的任务被认为是平等的。我想知道为什么会这样,以及是否有任何方法可以防止这种行为。

下面,我给出了一个重现此行为的单元测试(使用mstest)。如果取消注释Task.Delay并引入延迟,则测试通过。

    private async Task successTaskFn()
    {
        //await Task.Delay(250);
        MemoryStream ms = new MemoryStream();
        using (var sw = new StreamWriter(ms))
        {
            await sw.WriteLineAsync("Line1");
            await sw.WriteLineAsync("Line2");
        }
    }

    [TestMethod]
    public void TaskIdsCheck()
    {
        var t1 = successTaskFn();
        var t2 = successTaskFn();
        var t3 = successTaskFn();

        Assert.AreNotSame(t1, t2);
        Assert.AreNotSame(t1, t3);
        Assert.AreNotSame(t2, t3);
    }

在AreNotSame上测试失败,因为认为Task对象是相同的。

1 个答案:

答案 0 :(得分:3)

这里有两个重要的要点:

  • 异步方法同步运行,直到第一个正在等待尚未完成的等待表达式。
  • 已进行了一些优化以尽可能重用任务。这仅适用于完成的任务,然后仅当可以缓存结果时才可以。在您的情况下,您不处理任何返回值的任务-缓存“没有结果的已完成任务”很容易,因为您只需要其中之一即可。

因此,如果您有一个方法,它不执行任何操作(根本没有等待)并且仅返回Task,那么该方法将始终返回相同的{{ 1}}。 (不能保证,但这是当前的行为。)

如果您有确实正在等待的方法,则取决于所等待的东西。如果您等待的操作尚未完成,则异步机制需要进行一次复杂的操作才能使状态机装箱(第一次),安排继续操作,然后返回到调用方。

就您而言,您只是在等待对Task进行操作的结果。 MemoryStream对于这种操作是异步的没有意义,因此它的异步方法仅返回一个完成的任务-这使它像完全同步一样工作。

如果您使用:

MemoryStream

应该可以防止这种行为,但是我只会在极端情况下这样做。您完全不应该依靠依赖返回的任务的身份。