SDD(Sleep-Driven Development)

睡眠の重要性!!睡眠の重要性!!

SynchronizationContext.Currentでnullが返ってくる

躓きかけたのでメモ

C#で以下のようなメソッドを書いたとします。

//Task.Run(()=>Hoge())などで呼び出す
public void Hoge()
{
    //ネイティブUI操作
}

これを非同期(バッググラウンドスレッド)で触ろうとすると死にます。いわゆるUIスレッド以外でUI要素に触ってはいけない問題です。これを回避するにはUIスレッドで実行してやればいいだけです。

PCLの場合、呼び出し側が自分で制御できる場合は例えばTaskSchedulerクラスのFromCurrentSynchronizationContextを使えば問題ないわけですが

//var sheduler=TaskScheduler.FromCurrentSynchronizationContext();
//Task.Factory.StartNew(()=> { Hoge(); },CancellationToken.None,TaskCreationOptions.None, sheduler)で呼び出す
public void Hoge()
{
    //ネイティブUI操作
}

ライブラリ等で呼び出し側を自分で制御できない場合SynchronizationContext.Current.Postメソッドを使えばいいです。

しかしこれにも少し問題があって例えば以下のようなコードを書くと死にます。

//Task.Run(()=>Hoge())などで呼び出す
public void Hoge()
{
    SynchronizationContext.Current.Post((_=>
    {
        //ネイティブUI操作
    },null));
}

何故かと言うとSynchronizationContext.CurrentはUIスレッド以外で呼び出すとnullが返ってきてヌルポるからです。

これを回避するには必ずメインスレッド中で変数などに確保しておくのが手っ取り早いです。もしHoge()のクラスがバックグラウンドスレッド以外で初期化されるならフィールドに確保しておくのが一番簡単でしょう。

SynchronizationContext context=SynchronizationContext.Current;

//Task.Run(()=>Hoge())などで呼び出す
public void Hoge()
{
    context.Post((_=>
    {
        //ネイティブUI操作
    },null));
}

しかしクラスの初期化自体がバックグラウンドスレッドの場合はフィールドで初期化でもヌルポるかもしれません。

これを回避するには絶対にUIスレッドで実行されるところで初期化してstaticにもたせてあげればよさそうなのですが少し面倒ですね・・・