手动编写WCF代理时,带CancellationToken的方法能否正常取消?
关于WCF代理中CancellationToken触发时提前调用EndLongCall的问题
我来帮你理清这个WCF异步调用取消的核心问题:在WCF异步调用未完成(asyncToken.IsComplete为false)时直接调用EndLongCall()肯定会出问题,而且你的当前实现逻辑也存在漏洞,下面详细说明:
为什么直接提前调用EndLongCall会报错?
WCF的传统APM(异步编程模型)有严格的规则:你必须在IAsyncResult标记为完成后,才能调用对应的EndXXX方法。如果提前调用,WCF运行时会直接抛出InvalidOperationException,提示你“异步操作尚未完成”。这不是什么边缘情况,是WCF异步模型的硬性约束——不管你是因为取消令牌触发还是其他原因提前调用,这个异常都会发生。
而且你的当前代码里,WaitHandle.WaitAny返回后,你没有判断到底是取消令牌的等待句柄触发,还是调用完成的等待句柄触发。如果是取消令牌先触发,你直接调用EndLongCall就会立刻碰到这个异常,完全达不到“优雅停止等待”的目的。
不使用TPL的正确处理方式
既然你不想用TPL,那我们基于传统APM模型来实现可靠的取消逻辑,核心是在取消触发时终止WCF通道,避免资源泄漏,同时抛出取消异常告知调用方:
public Pack[] LongCall(CancellationToken ct) { var asyncToken = BeginLongCall(null, null); var waitIndex = WaitHandle.WaitAny(new[] { ct.WaitHandle, asyncToken.AsyncWaitHandle }); if (waitIndex == 0) // 取消令牌被触发 { // 强制终止WCF通道,取消未完成的调用并释放资源 ((ICommunicationObject)Channel).Abort(); // 抛出标准取消异常,让调用方可以捕获处理 throw new OperationCanceledException(ct); } else // 异步调用正常完成 { return EndLongCall(asyncToken); } }
关键细节说明:
- 为什么要调用
Abort()?因为WCF的传统APM没有提供“取消单个异步调用”的机制,如果你不终止通道,未完成的BeginLongCall会占用系统资源,而且你又不能调用EndLongCall(因为调用没完成)。Abort()会强制终止所有未完成的操作,释放关联的资源,避免泄漏。 - 注意:调用
Abort()后,这个Channel就彻底不能用了,如果后续还需要发起WCF调用,你需要重新创建代理实例。 - 如果你不想终止整个通道(比如还要复用这个代理),那传统APM下几乎没有优雅的方式取消单个调用——你只能等待调用自然完成,然后忽略结果,但这显然不符合你“停止等待”的需求。
额外提醒
永远不要跳过调用EndXXX方法——如果调用正常完成,必须调用EndLongCall来获取结果并释放资源;如果调用被取消,你通过Abort()终止通道,也相当于间接释放了未完成调用的资源。跳过EndXXX会导致WCF客户端资源泄漏,长期运行会引发性能问题。
内容的提问来源于stack exchange,提问作者Sean B
相关产品推荐
相关产品推荐

