【问题标题】:TaskScheduler.FromCurrentSynchronizationContext exception when called from worker thread从工作线程调用时的 TaskScheduler.FromCurrentSynchronizationContext 异常
【发布时间】:2014-03-05 10:36:50
【问题描述】:

不太清楚这里发生了什么 -

这段代码会导致问题,因为它首先从主线程调用(在 VS 的任务视图中验证)并调度任务,但是当在 UpdateSearchCache 中设置断点时,我们现在处于工作线程中 -不再是主要的!

从那里调用的后续 UI 代码在工作线程上执行时会失败。

这不是指定调度程序的重点吗?我错过了什么?

启动我们的应用程序时会调用此代码。它从我们的 PRISM 应用程序的引导程序调用并在 MainThread 上运行。

任务启动时SynchronizationContext.Current 不为空。

var currentScheduler = TaskScheduler.FromCurrentSynchronizationContext();
var ctx = SynchronizationContext.Current;
if (ctx == null)
    throw new NullReferenceException();

Task.Factory
    .StartNew(
        () =>
            SearchHelper.CacheSearchResults(_service.GetData())
    .ContinueWith(result => UpdateCache(result.Result), currentScheduler);

【问题讨论】:

  • 您的代码片段的第一行是否在主 UI 线程上执行?如果是这样,您是否在Form.Load 等处执行此操作,即在Application.Run 核心循环内?
  • 在保存之前,请执行以下操作:Debug.Assert(SynchronizationContext.Current != null)。它通过了吗?
  • 如果您使用.net 4.0,您可能会遇到这个问题:stackoverflow.com/q/11621372/495262
  • 确实我们正在使用(并且必须)4.0 :-(
  • SynchronizationContext.Current 在我们开始时不为空 - 相应地编辑了问题。

标签: c# .net c#-4.0 task-parallel-library


【解决方案1】:

TaskScheduler.FromCurrentSynchronizationContext 在调用线程上没有同步上下文时抛出 InvalidOperationException,即SynchronizationContext.Current 返回 null。

【讨论】:

  • 但是当它应该在当前线程中运行时,如何在工作线程上执行该方法,a.k.a. main?
  • @cacau - 延续计划在哪个线程上取决于您拥有的同步上下文。默认同步上下文安排任务在线程池上运行。但是,如果您遇到异常,则问题甚至会在您启动父任务之前发生,并且这里没有足够的代码来了解为什么会发生这种情况。
  • 在我们的应用程序启动时,在我们的 (PRISM) 引导程序中调用了这段被调用。问题相应编辑。至少在VS中我们可以看到一堆工作线程加上主线程在那个时候已经存在..
  • @cacau:您知道 SyncronisationContext.Current 不是全局变量,它是每个线程的属性。您可能希望从 UI 线程代码中捕获 TaskScheduler 并从背景线程中使用它。
  • @jdv-JandeVaan,他与TaskScheduler.FromCurrentSynchronizationContext 合作。这捕获了同步。调用时的上下文,它是非空的。
【解决方案2】:

这里发生了一些非常奇怪的事情。尝试以下方法。这是一种解决方法,但也可能有助于诊断问题:

var dispatcher = Dispatcher.CurrentDispatcher;
Debug.Assert(dispatcher == Application.Current.Dispatcher);

Task.Factory
    .StartNew(
        () => SearchHelper.CacheSearchResults(_service.GetData()))
    .ContinueWith(result => 
    { 
        // is the app's dispatcher still the same?

        Debug.Assert(dispatcher == Application.Current.Dispatcher);

        // explicitly use Dispatcher.BeginInvoke, that's what
        // DispatcherSynchronizationContext does behind the scene

        Application.Current.Dispatcher.BeginInvoke(new Action(
            () => UpdateCache(result.Result)));

    }, TaskContinuationOptions.ExecuteSynchronously);

【讨论】:

    猜你喜欢
    • 2014-10-06
    • 2011-03-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-26
    • 1970-01-01
    • 2012-11-24
    相关资源
    最近更新 更多