アプリケーションに問題があります。ある時点で、メインスレッドのSynchronizationContext.Currentがnullになります。孤立したプロジェクトで同じ問題を再現できません。私の実際のプロジェクトは複雑です。 WindowsフォームとWPFを混合し、WCF Webサービスを呼び出します。私の知る限り、これらは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がnullになる原因は何ですか?
編集:
@Hansがスタックトレースを要求しました。ここにあります:
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コールバック、オブジェクト状態、ブール値ignoreSyncCtx) at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task&cu rrentTaskSlot) 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、Binderinder、Object [] parameters、CultureInfoculture、Boolean skipVisibilityChecks) at System.Delegate.DynamicInvokeImpl( Object。] args) at System.Windows.Forms.Control.InvokeMarshaledCallbackDo(ThreadMethodEntry tme) at System.Windows.Forms.Control.InvokeMarshaledCallbackHelper(Obje ct 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.ControlNative Window.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) 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 MyApp.Framework.SharedUI.ApplicationBase.Start()のd:\ sources\s2\Framework\Sources\UI\SharedUI\ApplicationBase.cs:line 118 MyApp.App1.WinUI.HDA.Main()のd:\ sources\s2\App1\Sources\WinUI\HDA.cs:line 63
Slyは、WPF、WCF、およびTPLの混合が使用されている場合、まったく同じ動作に遭遇しました。メインスレッドの現在のSynchronizationContextは、いくつかの状況でnullになります。
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フォーラムの この投稿 によると、これは4.0のTPLで確認されたバグです。同僚は4.5で実行しており、この動作は見られません。
FromCurrentSynchronizationContextを使用してメインスレッドで静的シングルトンにTaskSchedulerを作成し、継続を作成するときに常にそのタスクスケジューラを参照することで、これを解決しました。例えば
Task task = Task.Factory.StartNew(() =>
{
// something
}
).ContinueWith(t =>
{
// ui stuff
}, TheSingleton.Current.UiTaskScheduler);
これにより、.net 4.0のTPLの問題が回避されます。
Update開発マシンに.net 4.5がインストールされている場合、4.0フレームワークをターゲットにしている場合でも、この問題は発生しません。 4.0のみがインストールされているユーザーも影響を受けます。
これが推奨される方法かどうかはわかりませんが、ここにSynchronizationContextの使用方法があります。
コンストラクター(メインスレッド)で現在のコンテキストのコピーを保存します。これにより、どのスレッドを使用していても、後で正しいコンテキストが保証されます(??)。
_uiCtx = SynchronizationContext.Current;
そして、後でタスクでそれを使用してメインUIスレッドと対話します
_uiCtx.Post( ( o ) =>
{
//UI Stuff goes here
}, null );
このためのクラスを作成しました。次のようになります。
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);