当您只能 运行 同步时满足异步接口方法
Satisfying an async interface method when you can only run sync
我正在使用作为 COM+ 对象实现的 "API"(我随意使用该术语)。据我所知,不支持 COM+ 提供的任何排队功能,但我对 COM+ 几乎一无所知。对它持保留态度。我只有一种方法可用:invoke
,它是同步的。
出于某种个人才华,我围绕此 COM+ 对象创建了一个 Web Api 包装器,它只有一个端点,使用请求的 post 主体调用 COM+ 对象。这让我可以改为对 Web 进行异步调用 Api(以及从我的应用程序中删除可怕的依赖项)。
现在,我正在创建一个库来基本上抽象所有这些乱七八糟的东西,但我决定实现一个提供者模式,您可以在其中选择直接使用 COM+ 或 Web Api 包装器。显然,我有一个接口,同时具有同步和异步方法。 Web Api 提供程序当然没有问题,因为您可以通过任何一种方式执行 HTTP 请求,但我在使用 COM+ 提供程序时遇到了瓶颈。因为那里没有异步选项。
所以,我有以下三种选择:
"Implement"带有NotImplementedException
的异步接口方法,违反了接口隔离原则
做一些不明智的事情,比如使用 Task.Run
,我知道在这种情况下它并不是真正的异步,并且基本上对任何针对我的库进行编程的人撒谎。
做一些事情,比如创建接口的同步和异步版本,这从接口隔离的角度来看更好,但从提供者模式的角度来看更差。如果我依赖异步接口,那么 COM+ 提供程序将永远无法使用,如果我依赖同步接口,那么我将永远无法使用 Web Api 提供程序的异步方法。
总而言之,我的一般问题是,如果您需要 运行 一些异步操作,但您需要使用的方法只能是 运行 同步操作,您会怎么做?如果没有真正的替代方案,那么满足接口要求的攻击性最小的方法是什么?
编辑
我忘了再提一个选项:
- 在异步实现中调用同步方法,然后简单地 return
Task.FromResult
得到结果。这意味着我不会拉一个新线程(这可能是网络应用程序中的一个问题),但我不确定这是否真的比 Task.Run
对消费者撒谎更好实际上是 "async".
我找到的解决方案是使用 Task.FromResult
到 return 和 Task
,即使没有执行任何异步操作。虽然这实际上不是异步的,但它满足接口的要求。然后我对该方法进行了评论,指出它将 运行 同步,并且在线程无法被阻塞的情况下(例如 GUI)应作为委托传递给 Task.Run
。使用 Task.Run
并不适用于所有情况(Web 应用程序,例如),并且是一个实施细节,应由库的使用者决定。
也就是说,要真正实现这一点,您需要处理三种 Task
场景:完成、出错和取消。这是执行所有这些操作的实际代码:
public Task<int> DoSomethingAsync(CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested)
{
return Task.FromCanceled<int>(cancellationToken);
}
try
{
return Task.FromResult<int>(DoSomething());
}
catch (Exception e)
{
return Task.FromException<int>(e);
}
}
首先,这确保操作没有被取消。如果有,取消的任务是 returned。然后,我们需要做的同步工作被包裹在一个 try..catch 块中。如果抛出异常,我们将需要 return 一个包含该异常的错误任务。最后,如果它正确完成,我们 return 完成任务。
我正在使用作为 COM+ 对象实现的 "API"(我随意使用该术语)。据我所知,不支持 COM+ 提供的任何排队功能,但我对 COM+ 几乎一无所知。对它持保留态度。我只有一种方法可用:invoke
,它是同步的。
出于某种个人才华,我围绕此 COM+ 对象创建了一个 Web Api 包装器,它只有一个端点,使用请求的 post 主体调用 COM+ 对象。这让我可以改为对 Web 进行异步调用 Api(以及从我的应用程序中删除可怕的依赖项)。
现在,我正在创建一个库来基本上抽象所有这些乱七八糟的东西,但我决定实现一个提供者模式,您可以在其中选择直接使用 COM+ 或 Web Api 包装器。显然,我有一个接口,同时具有同步和异步方法。 Web Api 提供程序当然没有问题,因为您可以通过任何一种方式执行 HTTP 请求,但我在使用 COM+ 提供程序时遇到了瓶颈。因为那里没有异步选项。
所以,我有以下三种选择:
"Implement"带有
NotImplementedException
的异步接口方法,违反了接口隔离原则做一些不明智的事情,比如使用
Task.Run
,我知道在这种情况下它并不是真正的异步,并且基本上对任何针对我的库进行编程的人撒谎。做一些事情,比如创建接口的同步和异步版本,这从接口隔离的角度来看更好,但从提供者模式的角度来看更差。如果我依赖异步接口,那么 COM+ 提供程序将永远无法使用,如果我依赖同步接口,那么我将永远无法使用 Web Api 提供程序的异步方法。
总而言之,我的一般问题是,如果您需要 运行 一些异步操作,但您需要使用的方法只能是 运行 同步操作,您会怎么做?如果没有真正的替代方案,那么满足接口要求的攻击性最小的方法是什么?
编辑
我忘了再提一个选项:
- 在异步实现中调用同步方法,然后简单地 return
Task.FromResult
得到结果。这意味着我不会拉一个新线程(这可能是网络应用程序中的一个问题),但我不确定这是否真的比Task.Run
对消费者撒谎更好实际上是 "async".
我找到的解决方案是使用 Task.FromResult
到 return 和 Task
,即使没有执行任何异步操作。虽然这实际上不是异步的,但它满足接口的要求。然后我对该方法进行了评论,指出它将 运行 同步,并且在线程无法被阻塞的情况下(例如 GUI)应作为委托传递给 Task.Run
。使用 Task.Run
并不适用于所有情况(Web 应用程序,例如),并且是一个实施细节,应由库的使用者决定。
也就是说,要真正实现这一点,您需要处理三种 Task
场景:完成、出错和取消。这是执行所有这些操作的实际代码:
public Task<int> DoSomethingAsync(CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested)
{
return Task.FromCanceled<int>(cancellationToken);
}
try
{
return Task.FromResult<int>(DoSomething());
}
catch (Exception e)
{
return Task.FromException<int>(e);
}
}
首先,这确保操作没有被取消。如果有,取消的任务是 returned。然后,我们需要做的同步工作被包裹在一个 try..catch 块中。如果抛出异常,我们将需要 return 一个包含该异常的错误任务。最后,如果它正确完成,我们 return 完成任务。