【问题标题】:Know if a method returning a Task is actually asynchronous知道返回 Task 的方法是否实际上是异步的
【发布时间】:2021-12-15 02:40:14
【问题描述】:

有些代码我无法更改:

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)。

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

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
    {
        // ...
    }
}

【问题讨论】:

  • 你不能依靠线程 id 来告诉你。你可能会得到一个不同的线程,你可能不会。任务确实有IsCompletedStatus,您可以使用它们。
  • @Crowcoder:我绝对是 100% 愚蠢的。只看 IsCompleted 显然是要走的路。

标签: c# asynchronous async-await task


【解决方案1】:

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

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

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

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

您的代码依赖于await 导致检查任务是否完成的行为,如果完成则继续正常运行。你可以这样做。这种方法会比你的方法好得多,但仍然不完全准确。

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

与您的解决方案相比,此方法更简单、更高效且不准确,但仍可能出现误报。如果一个方法实际上是异步的,但非常快,它可能会返回一个不完整的任务,然后在检查之前完成它。这不太可能,因此如果您知道如果它是异步的,它将长时间运行,或者如果它完成得如此之快,您对它是异步的就可以了,那么上述方法可能就足够了。 (请注意,Task.Yield 专门设计用于执行此操作。第一次检查时它不会完成,但之后会立即完成,因此返回该任务的方法或由它组成的方法可能会导致这种情况行为,即使它不是为了恶意欺骗而构建的。)

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

【讨论】:

  • 我实际上想调用该方法,因此,虽然名称不正确,但 IsAsync 方法按预期工作,而且我对方法有时可以异步,有时不能的事实没有任何问题。但是您使用 IsCompleted 的解决方案非常适合我的问题(我不知道为什么我之前没有想到它 - 我可能需要睡觉)。非常感谢您的宝贵时间。
猜你喜欢
  • 2019-07-22
  • 1970-01-01
  • 2016-10-28
  • 1970-01-01
  • 1970-01-01
  • 2021-05-11
  • 1970-01-01
  • 2020-02-21
  • 1970-01-01
相关资源
最近更新 更多