【问题标题】:Top Level Task Causes Error "The current SynchronizationContext may not be used as a TaskScheduler." [duplicate]顶级任务导致错误“当前 SynchronizationContext 可能无法用作 TaskScheduler。” [复制]
【发布时间】:2014-01-26 23:53:40
【问题描述】:

虽然我查看了 stackoverflow 和文档,但我还没有看到答案。

如果我从计时器处理程序或另一个任务中调用创建任务的代码,则会在下面的 ContinueWidth 上出现此错误。例如,如果它被包装在另一个任务中,该任务每隔一段时间(例如每 1 秒)创建任务,如下所示。我拥有高级任务的唯一原因是每隔一段时间创建这些低级任务。

//更高级别的任务创建

...

Task task = Task.Factory.StartNew(new Action(UpdateAllDuringInterval));

...

    private void UpdateAllDuringInterval()
    {
        Stopwatch stopWatch = new Stopwatch();

        do
        {
            // Start the stopwatch
            stopWatch.Start();

            // Create tasks
            List<Task> tasks = CreateTasksAndStart();

            // Wait for the tasks to complete if testing, since want to check results
            if (this._testMode)
            {
                Task[] taskArray = tasks.ToArray();
                Task.WaitAll(taskArray);
            }

            if (!_testMode)
            {
                // Get remaining time to complete interval and sleep for that time
                int remainingTimeInMilliseconds = this._pollingIntervalInMilliseconds -
                                                  (int) stopWatch.Elapsed.TotalMilliseconds;
                    // truncating milliseconds
                if (remainingTimeInMilliseconds > 0)
                    Thread.Sleep(remainingTimeInMilliseconds);
                        // will give time to CPU. Note that using Sleep used to be frowned upon but no longer due to advantages in multitasksing/CPU utilization
            }
        } while (!_testMode); // continue updating stocks once per interval if not testing
    }

    private List<Task> CreateTasksAndStart()
    {
        var tasks = new List<Task>();

        lock (syncLock)
        {
            for (int i = 0; i < _stocksToUpdate.Count; i++)
            {
                var item = _stocksToUpdate[i];
                var initialTask = Task.Factory.StartNew(() =>
                {
                    GetUpdatedStockInformation(item);
                });
                var continuationTask = initialTask.ContinueWith((antecendent) =>
                {
                    UpdateUIWithUpdatedStockInformation();
                }
                , CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion,
                TaskScheduler.FromCurrentSynchronizationContext());

                // For testing, we want to wait on the continuation task to make sure updates are done
                tasks.Add(continuationTask);
            }
        }
        return tasks;
    }

如果需要更多信息,请告诉我。随意给我这个代码的其他陷阱。谢谢!

【问题讨论】:

  • 异常准确,没有当前同步上下文。你想在 UI 线程上创建任务,你想稍后运行它。
  • 感谢 Hans 的好主意!

标签: c# wpf multithreading task


【解决方案1】:

原因很简单,但解决方案可能需要您重新设计一些任务的交互。您没有设置SynchronizationContext(即SynchronizationContext.Current 为空),因此无法从中创建TaskScheduler。一种选择是在您在 UI 线程上运行的位置调用 TaskScheduler.FromCurrentSynchronizationContext(),然后向下传递到构造延续的方法。另一个是创建任务并传递它们,以便 UI 线程可以附加延续。

通常认为有像_testMode 这样的标志是一种代码异味,大概只测试代码集。然后你得到了一些你并没有真正测试的交互,因为测试代码做一件事,但实际的应用程序做另一件事。

【讨论】:

  • 迈克 - 感谢您的解决方案。它很好地通过了这些方法,并且像一个魅力一样工作 - 非常感谢!您的其他反馈也受到赞赏。我需要为此考虑一个更好的解决方案。
  • 我观察到 SynchronizationContext.Current 即使对于控制台应用程序类型的项目也是空的。为什么会这样?至少它有一个控制台输出 UI,可以从本质上是后台线程的线程池线程更新。
  • @Rasik 所有应用程序都以空同步上下文开始。通常,您正在使用一些框架来为您设置它(Windows 窗体、wpf、asp.net 等)。标准控制台应用程序没有任何类型的框架可以为您设置,也不需要您提到的框架。
  • 非常感谢。拯救我的一天!
猜你喜欢
  • 2012-01-04
  • 1970-01-01
  • 1970-01-01
  • 2014-03-24
  • 2019-09-17
  • 2021-06-20
  • 1970-01-01
  • 2019-10-25
  • 2010-09-16
相关资源
最近更新 更多