TaskCompletionSource间歇性地无法完成NServiceBus和WCF

时间:2015-10-04 04:18:43

标签: multithreading wcf asynchronous task-parallel-library nservicebus

我对TaskCompletionSource有一个不寻常的问题让我感到困惑。一旦我调用TrySetResult,我有一个TaskCompletionSource等待任务完成。我在代码中的三个位置调用它:从WCF线程立即返回值到APM WCF BeginXXX EndXXX;从另一个WCF线程立即返回到APM;最后来自NServiceBus处理程序线程。

我从MS-PL提供的无处不在的ToAPM开始。 http://blogs.msdn.com/b/pfxteam/archive/2011/06/27/using-tasks-to-implement-the-apm-pattern.aspx

我注意到两个基于WCF的线程100%的时间都在工作。在100小时的硬测试中,还有大量的单元测试,我从来没有遇到过将完成的任务返回到AsyncCallback的单一故障。

从MS提供的ToAPM代码中,代码在已完成的任务上使用ContinueWith在启用计划的任务中调用AsyncCallback。

我还没解决的问题是NServiceBus线程在TaskCompletionSource对象上调用TrySetResult。我发现中断的时间,在未定义的时间段内,呼叫完全失败。我在调用和ContinueWith代码内部的代码中设置了断点。我总是在TrySetResult上获得断点,但有时只在ContinueWith代码中的代码上。

希望以下信息可以解释这个问题。

我使用带超时的CancellationTokenSource并设置结果以调用TaskCompletionSource obj上的TrySetResult。当上述调用无法将任务移至完成时,将触发超时代码。这个超时代码从来没有用过。它100%的成功。

有趣的是,在从NServiceBus线程调用TrySetResult的相同代码中,当它工作时,它就像调用TaskCompletionSource对象上的TrySetResult一样轻松调用取消对象的取消。 / p>

当一个人失败时他们都会失败。

然后经过不分青红皂白的时间再次发挥作用。

这是生产和QA环境中的WCF服务器,每个都显示相同的结果。

最奇怪的是,对于一个WCF连接,NServiceBus线程成功,另一个同时失败。然后有时两个都工作,然后都失败了。同样,所有这些都在同一时间。

我已经尝试过很多方法解决这个问题但无济于事:

  • 我在TaskCompletionSource + ContinueWith中包装了对TrySetResult的调用 - 失败
  • 我将调用包装在Task.Factory.StartNew中 - 失败
  • 我直接称呼它 - 失败

我真的不知道还有什么可以尝试的。

我进行检查以确保TaskCompletionSource obj未完成,并且在中断期间它不是。 我进行了检查,以确保在停机期间取消CancellationTokenSource对象或取消暂停,但事实并非如此。

我检查了调试器中的对象,看起来很好。

他们只是不工作有时

NserviceBus线程中是否存在不一致,有时会阻止调用工作? 我可以尝试一些线程封送吗?

我到处搜索,我没有看到提到这个问题。它有独特之处吗?

我完全感到困惑,需要一些想法。

1 个答案:

答案 0 :(得分:1)

从NServiceBus线程执行中删除调用。使用QueueUserWorkItem等线程或旋转自己的线程隔离对TrySetResult的调用。由于执行恢复使用该线程,您可能需要一些额外的线程来处理吞吐量。以太网旋转多个专用线程或使用线程池。我测试了在专用线程中调用TrySetResult并且它们可以工作。

以下是演示单个专用线程的代码:

    public static void Spin()
    {
        ClientThread = new Thread(new ThreadStart(() =>
        {
            while (true)
            {
                try
                {
                    if (!HasSomething.WaitOne(1000, false))
                        continue;

                    while (true)
                    {
                        WaitingAsyncData entry = null;
                        lock (qlocker)
                        {
                            if (!Trigger.Any())
                                break;

                            entry = Trigger.Dequeue();
                        }

                        if (entry == null)
                            break;

                        entry.TrySetResult("string");
                    }
                }
                catch
                {
                }
            }
        }));
        ClientThread.IsBackground = true;
        ClientThread.Start();
    }

这是ThreadPool示例代码:

    ThreadPool.QueueUserWorkItem(delegate
    {
        entry.TrySetResult("string");
    });

使用ThreadPool而不是静态线程可以提供更大的灵活性和可扩展性。