知道返回任务的方法是否实际上是异步的

Know if a method returning a Task is actually asynchronous

有些代码我无法更改:

protected virtual void Method1() { }
private void Method2() { /* ... */ }

public void Method3()
{
    Method1();
    Method2();
}

并且在调用 Method3() 时,我必须调用一个方法,在 Method1 的覆盖中返回一个 Task。但是,在那次通话之后,我需要知道 Method2 是否已被调用。

protected override async void Method1()
{
    await MyOtherMethodAsync();
    bool method2HasBeenCalledYet = ??
    if (method2HasBeenCalledYet)
    {
        // ...
    }
    else
    {
        // ...
    }
}

问题是,这取决于 MyOtherMethodAsync 实际上是异步的(比如 Task.Run())还是非异步的(例如 Task.CompletedTask)。

我有一个想法,每次测试它都有效,但我不知道是否有它不起作用的情况(编辑:有)或者如果有更优雅的解决方案(edit:显然有)。

protected override async void Method1()
{
    bool method2HasBeenCalledYet = await IsAsync(MyOtherMethodAsync);
    if (method2HasBeenCalledYet)
    {
        // ...
    }
    else
    {
        // ...
    }
}

private async Task<bool> IsAsync(Func<Task> asyncAction)
{
    int beforeThreadId = Thread.CurrentThread.ManagedThreadId;
    await asyncAction().ConfigureAwait(false);
    int afterThreadId = Thread.CurrentThread.ManagedThreadId;
    return beforeThreadId != afterThreadId;
}

--

使用@Crowcoder 和@Servy 给出的解决方案编辑

protected override async void Method1()
{
    var task = MyOtherMethodAsync();
    if (!task.IsCompleted)
    {
        await task;
        // ...
    }
    else
    {
        // ...
    }
}

所以在一般情况下,给定一个任意委托,问题是无解的。

您或许可以专门设计某些方法来允许此代码确定它们是 运行 同步还是异步,但是您可以尝试采用任何方法来确定方法是异步还是同步在某些情况下是不正确的,即使这些情况是边缘的。

首先,值得指出的是您调用方法 以确定它是否是异步的。如果你打算那样做,那很好,但通常我不希望我称为 IsAsync 的方法实际上 运行 它。

对于您的特定解决方案,它依赖于假设该方法不是从线程池线程调用的,或者调用延续的线程池线程只是 发生与众不同。它可能会有所不同,但它可能不会很容易。这也是一种比其他(也是不完美的)解决方案更昂贵的确定答案的方式。

您的代码依赖于 await 导致检查任务是否完成并正常继续的行为。你可以这样做。这样的方法会比你的好很多,但还是不完全准确。

bool InaccurateIsAsync(Func<Task> func) => func().IsCompleted;

此方法比您的解决方案更简单、更有效且不准确,但它仍然可能有漏报。如果一个方法实际上是异步的,但是 真的很快 ,它可能 return 一个未完成的任务,然后在你检查它之前完成它。这不太可能,所以如果您知道如果它是异步的,它会很长 运行ning,或者如果它完成得如此之快,您就可以接受它是异步的,那么上述方法可能就足够了。 (请注意,Task.Yield 是专门为执行此操作而设计的。第一次检查时它不会完成,但之后会立即完成,因此 return 执行该任务的方法,或组合的方法的,即使它不是为了恶意欺骗它而构建的,也可能导致这种行为。)

此解决方案不准确的另一个原因是,一种方法完全有可能有时 运行 同步,有时 运行 异步。仅仅因为您 运行 它一次并且它 return 完成了一项任务并不意味着它会在您下次调用它时完成。 (因此,如果您调用 IsAsync 以查看未来的调用是否会同步,您需要了解有关特定实现的一些信息才能知道它不会那样做)。