2011-11-23 50 views
87

私はTasksを使用して、ViewModelで長時間実行されるサーバー呼び出しを実行しています。結果はTaskScheduler.FromSyncronizationContext()を使用してDispatcherにマーシャルされます。例:現在のSynchronizationContextはTaskSchedulerとして使用できません

var context = TaskScheduler.FromCurrentSynchronizationContext(); 
this.Message = "Loading..."; 
Task task = Task.Factory.StartNew(() => { ... }) 
      .ContinueWith(x => this.Message = "Completed" 
          , context); 

これはアプリケーションを実行するとうまく動作します。現在のSynchronizationContextがTaskSchedulerとして使用することはできません

:私はResharperに私NUnitテストを実行するときしかし、私はとFromCurrentSynchronizationContextへの呼び出しでエラーメッセージが表示されます。

これは、テストがワーカースレッドで実行されるためです。テストがメインスレッドで実行されるようにするにはどうすればよいですか?他の提案は歓迎されます。

答えて

133

SynchronizationContextを指定する必要があります。これは私がそれを扱う方法です:

[SetUp] 
public void TestSetUp() 
{ 
    SynchronizationContext.SetSynchronizationContext(new SynchronizationContext()); 
} 
+0

が働いて、ありがとう! – anivas

+5

MSTestの場合:ClassInitializeAttributeでマークされたメソッドに上記のコードを置きます。 – SACO

+0

おしゃれなこと... –

11

リッチメルトンのソリューションは私のために働いていませんでした。これは私のTestInitialize関数が私のテストと同じように非同期であるため、awaitごとに現在のSynchronizationContextが失われているためです。これは、MSDNが指摘しているように、SynchronizationContextクラスは「ダム」で、すべてのスレッドをスレッドプールにキューするだけです。

SynchronizationContextがないときは何私のために働いたことは実際には(現在のコンテキストがヌルである場合には、ある)FromCurrentSynchronizationContextコールを飛ばしています。 UIスレッドがない場合は、最初に同期させる必要はありません。

TaskScheduler syncContextScheduler; 
if (SynchronizationContext.Current != null) 
{ 
    syncContextScheduler = TaskScheduler.FromCurrentSynchronizationContext(); 
} 
else 
{ 
    // If there is no SyncContext for this thread (e.g. we are in a unit test 
    // or console scenario instead of running in an app), then just use the 
    // default scheduler because there is no UI thread to sync with. 
    syncContextScheduler = TaskScheduler.Current; 
} 

私はどこの選択肢、よりこのソリューションは、より簡単な見つけ:

  • (依存性注入を経由して)のViewModelにTaskSchedulerを渡し、テストSynchronizationContextと「偽」UIを作成します。テストのためのスレッドは、それが価値があると私にとってはより多くの問題を抱えています

私はスレッドのニュアンスを失いますが、私はそうではありません明示的に私のOnPropertyChangedコールバックが特定のスレッドでトリガーするので、私はそれで大丈夫です。他の回答はnew SynchronizationContext()ですが、とにかくその目標にはそれほど良くはありません。

+0

あなたの 'else'ケースは、Windowsサービスアプリケーションでも失敗し、' syncContextScheduler == null' – FindOutIslamNow

0

は私がのSynchronizationContextを操作するための保証を持っているために、複数のソリューションを組み合わせています

using System; 
using System.Threading; 
using System.Threading.Tasks; 

public class CustomSynchronizationContext : SynchronizationContext 
{ 
    public override void Post(SendOrPostCallback action, object state) 
    { 
     SendOrPostCallback actionWrap = (object state2) => 
     { 
      SynchronizationContext.SetSynchronizationContext(new CustomSynchronizationContext()); 
      action.Invoke(state2); 
     }; 
     var callback = new WaitCallback(actionWrap.Invoke); 
     ThreadPool.QueueUserWorkItem(callback, state); 
    } 
    public override SynchronizationContext CreateCopy() 
    { 
     return new CustomSynchronizationContext(); 
    } 
    public override void Send(SendOrPostCallback d, object state) 
    { 
     base.Send(d, state); 
    } 
    public override void OperationStarted() 
    { 
     base.OperationStarted(); 
    } 
    public override void OperationCompleted() 
    { 
     base.OperationCompleted(); 
    } 

    public static TaskScheduler GetSynchronizationContext() { 
     TaskScheduler taskScheduler = null; 

     try 
     { 
     taskScheduler = TaskScheduler.FromCurrentSynchronizationContext(); 
     } catch {} 

     if (taskScheduler == null) { 
     try 
     { 
      taskScheduler = TaskScheduler.Current; 
     } catch {} 
     } 

     if (taskScheduler == null) { 
     try 
     { 
      var context = new CustomSynchronizationContext(); 
      SynchronizationContext.SetSynchronizationContext(context); 
      taskScheduler = TaskScheduler.FromCurrentSynchronizationContext(); 
     } catch {} 
     } 

     return taskScheduler; 
    } 
} 

使用法:

var context = CustomSynchronizationContext.GetSynchronizationContext(); 

if (context != null) 
{ 
    Task.Factory 
     .StartNew(() => { ... }) 
     .ContinueWith(x => { ... }, context); 
} 
else 
{ 
    Task.Factory 
     .StartNew(() => { ... }) 
     .ContinueWith(x => { ... }); 
} 
関連する問題