【问题标题】:TaskCompletionSource intermittently does not complete with NServiceBus and WCFTaskCompletionSource 间歇性地不使用 NServiceBus 和 WCF 完成
【发布时间】:2015-10-04 04:18:42
【问题描述】:

我对 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 obj 上的 TrySetResult 一样轻松调用取消对象的 Cancel。

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

然后在不分青红皂白的一段时间后再次工作。

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

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

我尝试了很多方法来解决这个问题,但无济于事:

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

我真的不知道还能尝试什么。

我进行了检查以确保 TaskCompletionSource obj 未完成,并且在中断期间未完成。 我进行了检查以确保 CancellationTokenSource 对象在中断期间没有被取消或有取消挂起,但它没有。

我检查了调试器中的对象,它们看起来不错。

他们只是不工作有时

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

我到处搜索,但没有看到有人提到这个问题。它是独一无二的吗?

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

【问题讨论】:

  • 您需要发布所有相关代码。

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


【解决方案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.QueueUserWorkItem(delegate
    {
        entry.TrySetResult("string");
    });

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-01-19
    • 2014-11-09
    • 2012-08-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多