3

我一直在尝试了解如何基于新的 .NET 4.5 CancellationToken 机制来实现 WCF 调用取消。我发现的所有示例都不是基于 WCF 的,并且不跨越服务边界。

我的服务:

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class MyService : IMyService
{   
    public void LongOperation()
    {
        // do stuff that takes ages...
        // please cancel me!
    }
}

我的客户(桌面应用程序):

使用自动生成的代理:

private async void DoLongRunningTaskAsync()
{
    var asyncTask = _myService.LongOperationAsync();
    await asyncTask;
}

如何取消此任务?请提供适用于 .NET 4.5+ 上的 WCF 的具体示例

编辑:

下面的答案似乎表明由于技术原因这是不可能的。所以,想象一下,我创建了我的服务ContextMode=Session,然后设置了一个静态变量(在单独的服务调用中)调用cancellationPending=true,我的原始调用在这个阶段仍在运行,它会定期检查该变量。我还是不能取消吗?这仍然是不可能的吗?如果是这样,为什么?

4

3 回答 3

4

用两个词来说——这是不可能的。

如果您了解 WCF 调用的本质,那就很明显了。

查看您的示例,我们通常看到的是:两台独立的机器 A 和 B,通过网络连接。在机器 A 上,您有一些组件侦听套接字,当它收到命令时,它开始同步长计算RemoteCall()

在机器 B 上,您有一个客户端,实际上有两个自动生成的方法

BeginRemoteCall->IAsyncResult+ EndRemoteCall(IAsyncResult).

新的基于任务的异步调用版本(aka RemoteCallAsync()->Task)本质上是完全一样的,你总是可以使用创建自己的任务TaskFactory.FromAsync(BeginRemoteCall,EndRemoteCall)->Task

当您BeginRemoteCall在客户端上调用时,您将命令发送到第二台机器“请开始计算”,然后添加一个回调,当计算完成并得到结果时将调用该回调。在内部,此异步实现使用 I/O 完成端口并允许您的客户端是非阻塞的。

但是,如果服务器端的操作已经启动,则无法取消它。你可能对结果不感兴趣,你可能会忽略你的回调,但无论如何服务器端的计算都会完成。

而已

于 2015-10-28T16:42:14.153 回答
3

如前所述。无法跨越服务边界并在服务器端取消。

如果要取消客户端的任务,可以使用 Microsoft.VisualStudio.Threading.ThreadingTools 的WithCancellation扩展方法

它是Visual Studio SDK的一部分,或者您也可以从Nuget获取它。

 CancellationTokenSource ct = new CancellationTokenSource();
 ct.CancelAfter(20000);
 var asyncTask = _myService.LongOperationAsync();
 await asyncTask.WithCancellation(ct.Token);
于 2015-10-28T16:37:52.770 回答
1

编辑问题后,我发现取消逻辑电平是可以接受的。如果是这样,可以尝试以下算法。

1)创建一个服务InstanceContextMode=InstanceContextMode.PerSession,这样可以保证您拥有相同的服务实例来服务后续请求。

2)启动新会话使用标记为的服务操作OperationContract(IsInitiating = True)

3)创建为服务成员。不是静态的。这将是服务状态

CancellationTokenSource ct = new CancellationTokenSource();

4) 在每个服务方法中,您必须在任务中开始新的计算,并将取消令牌作为该任务的参数。

[ServiceContract(SessionMode = SessionMode.Allowed)]
public interface ICalculator
{
   [OperationContract(IsInitiating = True)]
   Bool Begin()

   [OperationContract(IsInitiating = false)]
   double Add(double n1, double n2)
}

[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)]
public class CalculatorService : ICalculator
{
    CancellationTokenSource cts = new CancellationTokenSource();

    public double Add(double n1, double n2)
    {
        var cancellationToken = cts.Token;

        var t = Task<double>.Factory.StartNew(() => { 
           // here you can do something long
           while(true){
           // and periodically check if task should be cancelled or not
           if(cancellationToken.IsCancellationRequested)
             throw new OperationCanceledException();

           // or same in different words
           cancellationToken.ThrowIfCancellationRequested();
           }
           return n1 + n2;
        }, cancellationToken);

        return t.Result;
    }

    public Bool Begin(){}
}

这是 WCF 会话的简要说明

所有的例子都只是我的想法,如果它真的有效,还没有尝试过。但是你可以试试。

于 2015-10-29T14:49:52.347 回答