2011-01-11 6 views
40

Ho un problema nella mia applicazione: ad un certo punto, SynchronizationContext.Current diventa nullo per il thread principale. Non riesco a riprodurre lo stesso problema in un progetto isolato. Il mio vero progetto è complesso; mescola Windows Form e WPF e chiama servizi Web WCF. Per quanto ne so, questi sono tutti i sistemi che possono interagire con SynchronizationContext.In che modo SynchronizationContext.Current del thread principale diventa nullo in un'applicazione Windows Forms?

Questo è il codice del mio progetto isolato. La mia vera app fa qualcosa che assomiglia a questo. Tuttavia, nella mia app reale, SynchronizationContext.Current è nullo sul thread principale quando viene eseguita l'attività di continuazione.

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()); 
} 

cosa potrebbe causare la SynchronizationContext.Current del filo principale per diventare nulla?

Edit:

@Hans chiesto l'analisi dello stack. Eccolo:

 

    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 

+0

Impostare un punto di interruzione su UpdateGUI e pubblicare la traccia dello stack. –

+0

@Hans: UpdateGUI() è nel mio progetto di esempio. Nel mio vero progetto, il metodo è chiamato HandleTaskError(). Ho postato la traccia dello stack nella mia domanda. – Sylvain

+2

Sembra del tutto normale. Non ho alcuna spiegazione sul perché TaskScheduler.FromCurrentSynchronizationContext() non funzioni. Questo è quello che fornisce l'SC per la chiamata Control.InvokeMarshaledCallback(). Verifica già un valore nullo. Supponendo che si stia utilizzando .NET 4.0 –

risposta

9

Non so se questo è il metodo preferito, ma qui è come io uso lo SynchronizationContext:

In te costruttore (thread principale) salvare una copia del contesto attuale, in questo modo si sono garantiti (??) per avere il giusto contesto in seguito, non importa quale thread sei su.

_uiCtx = SynchronizationContext.Current; 

E più tardi nel vostro compito usarlo per fare interagire con il principale thread UI

_uiCtx.Post((o) => 
{ 
//UI Stuff goes here 
}, null); 
+3

Ciò funzionerebbe sicuramente ma sarebbe una soluzione alternativa. Mi piacerebbe capire come il thread principale abbia perso il suo SynchronizationContext. – Sylvain

43

Sly, ho incontrato lo stesso identico comportamento quando una miscela di WPF, WCF e TPL è Usato. Il SynchronizationContext corrente del thread principale diventerà nullo in alcune situazioni.

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(); 

Secondo this post sui forum MSDN, questo è un bug confermato nel TPL in 4,0. Un collaboratore è in esecuzione su 4.5 e non vede questo comportamento.

Abbiamo risolto questo problema creando un TaskScheduler in un singleton statico con il thread principale utilizzando FromCurrentSynchronizationContext e quindi facendo sempre riferimento a quell'utilità di pianificazione quando si creano continuazioni. Ad esempio

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

Ciò evita il problema nel TPL su .net 4.0.

Aggiornamento Se si dispone di .NET 4.5 installato sul computer di sviluppo, non si vedrà questo problema anche se si prendono di mira il quadro 4.0. I tuoi utenti che hanno solo 4.0 installati saranno comunque interessati.

+1

Ho riscontrato questo errore: ho pubblicato un breve programma Winforms che dimostra un modo semplice per riprodurre il problema. http://stackoverflow.com/questions/11621372/synchronizationcontext-current-is-null-in-continuation-on-the-main-ui-thread –

+1

targeting 4.5.2 qui, è ancora visualizzato questo messaggio di errore. Non su tutte le attività però ... – CularBytes

7

Ho creato una classe per questo. Ecco come si presenta:

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(); 
    } 
} 

All'avvio della mia applicazioni Mi chiamano UIContext.Initialize()

E quando ne ho bisogno in un compito ho appena messo UIContext.Current come TaskScheduler.

Task.Factory.StartNew(() => 
{ 
    //Your code here 
}, CancellationToken.None, TaskCreationOptions.None, UIContext.Current); 
+0

Ha funzionato alla grande su un progetto Unity (5.5) che stava avendo questo problema esatto. Grazie! – jeromeyers

Problemi correlati