从另一个线程

时间:2015-11-09 04:26:56

标签: c# .net multithreading wcf duplex

好的,我的机智已经结束了这件事。我有一个WCF双工服务。以下是架构的工作原理:

  1. 客户端打开与端点的连接并提供回调实现
  2. 该服务接受该请求并在其他线程上执行某些操作(可能是1秒钟可能是2分钟,这就是我不使用异步操作的原因)
  3. 处理完成后,它会调用客户端的回调
  4. 问题是当服务调用回调时,似乎没有任何反应。没有错误,没有任何东西。经过进一步调查后,我在服务器跟踪中发现了一个异常:

    The I/O operation has been aborted because of either a thread exit or an application request
    

    这在尝试执行回调后立即发生。

    客户端永远不会收到回复或关闭。发生的一切都是,因为初始请求是在与主要线程不同的线程上进行的,所以它只是永远等待那个线程完成。

    最奇怪的是,如果我尝试在客户端调用的操作中调用回调,而不进入另一个线程,一切正常 - 回调被成功调用,这让我相信我已经配置服务正确,但有线程/死锁问题。

    以下是我如何调用该服务:

    SubmissionServiceClient client = CreateClientInstance();
            client.Open();
            Guid executionId = await client.SubmitAsync(submission);
            submissionCompletionSource.Task.Wait(); //waits for the callback to be called (I omitted the extra wiring code for better readability)
            client.Close();
    
    private SubmissionServiceClient CreateClientInstance()
    {
        NetHttpBinding binding = new NetHttpBinding();
        binding.WebSocketSettings.TransportUsage = WebSocketTransportUsage.Always;
        EndpointAddress endpointAddress = new EndpointAddress("ws://localhost:9080/SubmissionRouter");
        InstanceContext instanceContext = new InstanceContext(this);
        SubmissionServiceClient submissionServiceClient = new SubmissionServiceClient(instanceContext,binding,endpointAddress);
        return submissionServiceClient;
    }
    

    这是回调操作:

    public void SubmissionProcessed(SubmissionResultDto result)
            {
                submissionCompletionSource.TrySetResult(result);
            }
    

    这是客户端调用的服务操作:

    public Guid Submit(SubmissionDto submission, ISubmissionCallback callback)
            {
                ExecutionDto execution = new ExecutionDto()
                {
                    Id = Guid.NewGuid(),
                    Submission = submission
                };
                RequestExecution(execution); //Queues the execution of the operation
                submissions.Add(execution.Id, callback);
    
                return execution.Id;
            }
    

    这是服务调用客户端回调的地方(此方法在与初始请求的线程不同的线程上执行):

                    ISubmissionCallback callback = submissions[submissionResult.ExecutionId];
                        callback.SubmissionProcessed(submissionResult);
    

    服务行为:

    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Reentrant)]
    

    正如您在提交时看到的那样,该服务将回调存储在字典中,并与id配对,稍后用它来检索回调并调用它。我相信它失败的原因是因为我试图在另一个线程上这样做。

    修改 我在服务中添加了一个Ping操作,该操作调用另一个线程,该线程挂起3秒钟,然后在客户端上调用Pong函数。

    public void Ping()
            {
                var callback = OperationContext.Current.GetCallbackChannel<ISubmissionCallback>();
                Task.Run(() =>
                {
                    System.Threading.Thread.Sleep(3000);
                    callback.Pong();
                });
            }
    

    客户端:     课程     {         static void Main(string [] args)         {             NetHttpBinding binding = new NetHttpBinding();             binding.WebSocketSettings.TransportUsage = WebSocketTransportUsage.Always;             EndpointAddress endpointAddress = new EndpointAddress(“ws:// localhost:9080 / SubmissionRouter”);             InstanceContext instanceContext = new InstanceContext(new Callback());             SubmissionServiceClient submissionServiceClient = new SubmissionServiceClient(instanceContext,binding,endpointAddress);

            submissionServiceClient.Ping();
    
            Console.Read();
        }
    
        public void SubmissionProcessed(SubmissionResultDto result)
        {
            throw new NotImplementedException();
        }
        class Callback : ISubmissionServiceCallback
        {
            public void Pong()
            {
                Console.WriteLine("Pong!");
            }
    
            public void SubmissionProcessed(SubmissionResultDto result)
            {
    
            }
        }
    }
    

    这实际上成功了。我设法在客户端收到我的答复。我现在正式完全迷失了。

1 个答案:

答案 0 :(得分:3)

如果您阻止导致死锁的submissionCompletionSource.Task.Wait();的UI线程。默认情况下,WCF回调发生在UI线程上,您可以使用CallbackBehaviorAttribute

更改行为
[CallbackBehaviorAttribute(UseSynchronizationContext=false)]
class Callback : ISubmissionServiceCallback
{
    public void Pong()
    {
        Console.WriteLine("Pong!");
    }

    public void SubmissionProcessed(SubmissionResultDto result)
    {

    }
}

或者不阻止UI线程。

SubmissionServiceClient client = CreateClientInstance();
client.Open();
Guid executionId = await client.SubmitAsync(submission);
await submissionCompletionSource.Task; //awaits for the callback to be called without blocking the UI.
client.Close();