实现异步方法同步执行

3

我有一个接口需要我实现一个(异步的)任务:

namespace Microsoft.Owin.Security.Infrastructure
{
    public interface IAuthenticationTokenProvider
    {
        ..
        Task CreateAsync(AuthenticationTokenCreateContext context);
        ..
    }
}

我想实现这个方法,但我没有等待任何东西。虽然我必须等待一些东西或返回一些任务,否则我的代码无法编译。

public class MyImplementation: IAuthenticationTokenProvider
{
    public async Task CreateAsync(AuthenticationTokenCreateContext context) 
    {
        /// do something synchronously.

        await something here? ??
    }
}

我的问题是:如何在不异步执行任务的情况下实现这个方法?我已经找到了一些解决方案(使用 task.Yield()Task.Run(() => {}))),但我还不能完全理解什么是正确的实现方式和原因。


如果您没有等待任何东西,您的代码应该可以编译成功 - 它只是不会实际上是异步的。如果在您的情况下基本上有一个快速实现,不执行任何IO操作,那么这可能是可以接受的 - 但否则,您真的应该异步地处理事情... - Jon Skeet
如果我这样做,在编译时会收到警告:“异步方法缺少'await'运算符,将以同步方式运行”。为什么会出现这个警告?这为什么很重要? - Clark Kent
警告已经说明了问题 - 你有一个异步方法,但它同步完成。在你的情况下,这可能是你想要的。如果不知道 CreateAsync 方法的具体内容,很难判断是否需要这样做。如果它总是很快完成,那么就没问题了,你可以只针对这段代码禁止警告。 - Jon Skeet
让我们看看我是否理解正确。在我的情况下,我想编写以下代码:public Task CreateAsync(AuthenticationTokenCreateContext context) { return Task.FromResult(0); }因为它不是异步的? - Clark Kent
如果您实际上不想执行任何异步操作,那么可以这样做。但是,我们不知道同步运行所有代码是否适当,因为我们不知道该代码是什么。正如hvd在评论中提到的那样,在异常处理方面,这种方法效果不佳 - 异步方法通常会返回一个故障任务而不是抛出异常,除了可能参数验证。 - Jon Skeet
我明白了。我认为我的问题已经得到了回答。谢谢! - Clark Kent
2个回答

2

不需要等待即可实现该方法:

public async Task<Foo> GetFooAsync()
{
    return new Foo();
}

即使您不等待任何内容也没有关系。异步方法不需要等待任何东西(尽管编译器会警告您未等待任何内容)。

如果您不想要异步方法的开销(与异步“状态机”相关的成本可能不可取),您可以

public Task<Foo> GetFooAsync()
{
    return Task.FromResult(new Foo());
}

1
不要担心成本(因为那可能是微观优化),而要担心如果 new Foo() 抛出异常所产生的影响差异:在 async 版本中,会返回一个任务,指示它未能成功完成。而在另一个版本中,则根本没有返回任何任务。 - user743382
问题在于我没有使用Task<T>,而是只有一个Task。因此它是一个异步的void。因此,它没有任何结果可以创建。 - Clark Kent
@ClarkKent Task<T> 实现了 Task,因此您可以只返回 Task.FromResult(0) 来满足 Task 的(非泛型)返回类型。任务的“值”将不会被使用。 - spender

0
我的解决方案是返回一个代表已经执行的同步方法结果的Task。这样,您可以保持异步方法在异常抛出时的语义,而无需使用异步状态机。有趣的是,我实际上是出于同样的原因在同一接口上做到了这一点。
我有一个扩展方法:
    public static Task AsCompletedTask<T>(this Action<T> func, T arg)
    {
        var tcs = new TaskCompletionSource<VoidResult>();
        try
        {
            func(arg);
            tcs.SetResult(voidResult);
        }
        catch (Exception e)
        {
            tcs.SetException(e);
        };

        return tcs.Task;
    }

然后我通过以下方式调用:

    public Task CreateAsync(AuthenticationTokenCreateContext context)
    {
        return ((Action<AuthenticationTokenCreateContext>)Create).AsCompletedTask(context);
    }

顺便说一下,我讨厌那个名字。如果有更好的建议,我全听着呢。


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