Отлично работал над проектом Unity (5.5), в котором была именно эта проблема. Спасибо!

я проблема в приложении: в какой-то момент SynchronizationContext.Current становится нулевым для основного потока. Я не могу воспроизвести ту же проблему в изолированном проекте. Мой настоящий проект сложен; он смешивает Windows Forms и WPF и вызывает веб-службы WCF. Насколько я знаю, это все системы, которые могут взаимодействовать с SynchronizationContext.

Это код из моего изолированного проекта. Мое настоящее приложение делает что-то похожее на это. Тем не менее, в моем реальном приложении SynchronizationContext.Current имеет значение null в главном потоке, когда выполняется задача продолжения.

private void button2_Click(object sender, EventArgs e)
{
    if (SynchronizationContext.Current == null)
    {
        Debug.Fail("SynchronizationContext.Current is null");
    }

    Task.Factory.StartNew(() =>
    {
        CallWCFWebServiceThatThrowsAnException();
    })
    .ContinueWith((t) =>
    {

        //update the UI
        UpdateGUI(t.Exception);

        if (SynchronizationContext.Current == null)
        {
            Debug.Fail("SynchronizationContext.Current is null");
        }

    }, CancellationToken.None, 
       TaskContinuationOptions.OnlyOnFaulted,
       TaskScheduler.FromCurrentSynchronizationContext());
}

Что может привести к тому, что SynchronizationContext.Current основного потока станет нулевым?

Редактировать:

@ Ханс спросил трассировку стека. Вот:

   at MyApp.Framework.UI.Commands.AsyncCommand.HandleTaskError(Task task) in d:\sources\s2\Framework\Sources\UI\Commands\AsyncCommand.cs:line 157
   at System.Threading.Tasks.Task.c__DisplayClassb.b__a(Object obj)
   at System.Threading.Tasks.Task.InnerInvoke()
   at System.Threading.Tasks.Task.Execute()
   at System.Threading.Tasks.Task.ExecutionContextCallback(Object obj)
   at System.Threading.ExecutionContext.runTryCode(Object userData)
   at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
   at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot)
   at System.Threading.Tasks.Task.ExecuteEntry(Boolean bPreventDoubleExecution)
   at System.Threading.Tasks.SynchronizationContextTaskScheduler.PostCallback(Object obj)
   at System.RuntimeMethodHandle._InvokeMethodFast(IRuntimeMethodInfo method, Object target, Object[] arguments, SignatureStruct& sig, MethodAttributes methodAttributes, RuntimeType typeOwner)
   at System.RuntimeMethodHandle.InvokeMethodFast(IRuntimeMethodInfo method, Object target, Object[] arguments, Signature sig, MethodAttributes methodAttributes, RuntimeType typeOwner)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks)
   at System.Delegate.DynamicInvokeImpl(Object[] args)
   at System.Windows.Forms.Control.InvokeMarshaledCallbackDo(ThreadMethodEntry tme)
   at System.Windows.Forms.Control.InvokeMarshaledCallbackHelper(Object obj)
   at System.Threading.ExecutionContext.runTryCode(Object userData)
   at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Windows.Forms.Control.InvokeMarshaledCallback(ThreadMethodEntry tme)
   at System.Windows.Forms.Control.InvokeMarshaledCallbacks()
   at System.Windows.Forms.Control.WndProc(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
   at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
   at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
   at System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr dwComponentID, Int32 reason, Int32 pvLoopData)
   at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
   at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
   at System.Windows.Forms.Application.Run(Form mainForm)
   at MyApp.Framework.SharedUI.ApplicationBase.InternalStart() in d:\sources\s2\Framework\Sources\UI\SharedUI\ApplicationBase.cs:line 190
   at MyApp.Framework.SharedUI.ApplicationBase.Start() in d:\sources\s2\Framework\Sources\UI\SharedUI\ApplicationBase.cs:line 118
   at MyApp.App1.WinUI.HDA.Main() in d:\sources\s2\App1\Sources\WinUI\HDA.cs:line 63

 Hans Passant11 янв. 2011 г., 18:07
Установите точку останова на UpdateGUI и опубликуйте трассировку стека.
 Hans Passant11 янв. 2011 г., 21:43
Выглядит совершенно нормально. У меня нет объяснения, почему TaskScheduler.FromCurrentSynchronizationContext () не работает. Это тот, который предоставляет SC для вызова Control.InvokeMarshaledCallback (). Это уже проверяет на ноль. Предполагая, что вы используете .NET 4.0
 Sylvain23 июл. 2012 г., 18:16
@MichelAyres: мы так и не нашли причину и решили обойти проблему, скопировавSynchronizationContext.Current к_syncContext переменная. Затем в вызове задачи мы делаемif (SynchronizationContext.Current == null) { SynchronizationContext.SetSynchronizationContext(_syncContext); }, Вы смотрели на ответ @ Dan? Я никогда не пробовал, поэтому я до сих пор не принял его ответ. Если вы тестируете его с .Net 4.5 и если он работает, дайте мне знать, и я приму ответ Дэна.
 Hans Passant11 янв. 2011 г., 21:53
"Диспетчер", вы смешиваете Winforms и WPF-код?
 Michel Ayres23 июл. 2012 г., 15:09
@ Хитрый, ты получил ответ на эту проблему? У меня естьтакой же выпуск здесь>. <

Ответы на вопрос(3)

я столкнулся с точно таким же поведением, когда используется смесь WPF, WCF и TPL. Текущий SynchronizationContext основного потока станет нулевым в нескольких ситуациях.

var context = SynchronizationContext.Current;

// if context is null, an exception of
// The current SynchronizationContext may not be used as a TaskScheduler.
// will be thrown
TaskScheduler.FromCurrentSynchronizationContext();

Согласно сэта почта на форумах msdn это подтвержденная ошибка в TPL в 4.0. Коллега работает на 4.5 и не видит этого поведения.

Мы решили эту проблему, создав TaskScheduler в статическом синглтоне с основным потоком, используя FromCurrentSynchronizationContext, и затем всегда ссылаемся на этот планировщик задач при создании продолжений. Например

Task task = Task.Factory.StartNew(() =>
  {
    // something
  }
).ContinueWith(t =>
  {
    // ui stuff
  }, TheSingleton.Current.UiTaskScheduler);

Это позволяет избежать проблемы в TPL на .net 4.0.

Обновить Если на вашем компьютере разработки установлен .net 4.5, вы не увидите эту проблему, даже если вы нацелены на платформу 4.0. Ваши пользователи, у которых установлена ​​только 4.0, будут затронуты.

 Matt Smith24 июл. 2012 г., 17:47
Я столкнулся с этой ошибкой - я опубликовал короткую программу Winforms, которая демонстрирует простой способ воспроизвести проблему.stackoverflow.com/questions/11621372/...
 CularBytes20 мар. 2016 г., 15:10
при таргетинге 4.5.2 здесь все еще появляется это сообщение об ошибке. Не на все задачи, хотя ...

Я создал класс для этого. Это выглядит так:

public class UIContext
{
    private static TaskScheduler m_Current;

    public static TaskScheduler Current
    {
        get { return m_Current; }
        private set { m_Current = value; }
    }

    public static void Initialize()
    {
        if (Current != null)
            return;

        if (SynchronizationContext.Current == null)
            SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());

        Current = TaskScheduler.FromCurrentSynchronizationContext();
    }
}

При запуске моего приложения я вызываю UIContext.Initialize ()

И когда мне это нужно в задаче, я просто помещаю UIContext.Current как TaskScheduler.

Task.Factory.StartNew(() =>
{
    //Your code here
}, CancellationToken.None, TaskCreationOptions.None, UIContext.Current);
 argyle08 мар. 2017 г., 20:35
Отлично работал над проектом Unity (5.5), в котором была именно эта проблема. Спасибо!

что это предпочтительный метод, но вот как я использую SynchronizationContext:

В вашем конструкторе (основной поток) сохраните копию текущего контекста, таким образом вы гарантированно (??) будете иметь правильный контекст позже, независимо от того, в каком потоке вы находитесь.

_uiCtx = SynchronizationContext.Current;

И позже в вашей Задаче используйте его для взаимодействия с основным потоком пользовательского интерфейса.

_uiCtx.Post( ( o ) =>
{
 //UI Stuff goes here
}, null );
 Sylvain11 янв. 2011 г., 21:18
Это будет работать наверняка, но это будет обходной путь. Я хотел бы выяснить, как основной поток потерял свой SynchronizationContext.

Ваш ответ на вопрос