2011-09-02 7 views
12

私は次のようなことをしたい - 基本的に別のスレッドでコールバックを呼び出す非同期操作を呼び出していて、インラインで完了するのを待っていたい。私の心配は、スレッド間で共有される変数の変更(bar &イベント)は、たとえばレジスタに格納されているために同期されない可能性があるということです。メンバー変数であれば、それらを揮発性にすることはできますが、スタック上に作成されたローカル変数にはvolatileを使用できません。私はメンバ変数を使用することができますが、私はそのクリーナーはすべてのローカルを維持することでクラスを混乱させないと思います。(コールバッククロージャを使用して)スレッド間でローカル変数を共有するのは安全ですか?

Bar bar = null; 
ManualResetEvent event = new ManualResetEvent(false); 

foo.AsyncOperation(new Action(()=>{  
    // This delegate will be called in another thread 
    bar = ... 
    event.Set(); 
})); 

event.WaitOne(timeout); 
// use bar 
+2

注: 'AsyncOperation'と' WaitOne'の間に何かを行う場合を除き、あなたにも同期 –

+0

この質問は関連して、それを実行する可能性がありますhttp://stackoverflow.com/questions/6581848/memory-barrier-ジェネレータ/ 6932271#6932271 –

+0

@Marc - 良い点ですが、APIは本質的に非同期です。これは、ネットワーク経由でサーバーに送信されるメッセージに基づいています。 AsyncOperatonでは、メッセージが設定され、応答があるとコールバックに通知されます。ほとんどの場合、非同期で使用しますが、この特定のケースでは結果を待っています。返事が来ない場合は、WaitOneにタイムアウトを含めます。 – Shane

答えて

6

はい、正しく動作します。ここ

http://www.albahari.com/threading/part4.aspx

The following implicitly generate full fences:Setting and waiting on a signaling construct

読み、シグナリングにManualResetEventが含まれて構築します。

あなたはfull fenceが同じページで、何であるかを知りたい場合は、次の

フルメモリバリアの最も簡単な命令の並べ替えのいずれかの種類を防ぎ、完全なメモリ バリア(フルフェンス)ですフェンス またはそのフェンスの周囲にキャッシュする。 Thread.MemoryBarrierを呼び出すと フルフェンスが生成されます。

+0

クール; 'WaitOne'がフェンスとして機能するなら、それは安全です。私は、しかし、MSDNはこれを文書化するだろうhttp://msdn.microsoft.com/en-us/library/58195swd.aspx –

+2

@Marcスレッドセーフとフェンスに関するmsdnのドキュメントは、私が正しく覚えている場合、かなり不足している、しかし、http://msdn.microsoft.com/en-us/library/ms686355(v=vs.85).aspx '次の同期関数は、メモリの順序を保証するために適切な障壁を使います:' 'Wait functions'と' '関数信号同期オブジェクト ' – xanatos

+0

がソートされ、私は私の答えを削除します –

4

私はあなたのコードが動作すると思います - 閉鎖は、彼らはちょうど(ManualReseetEventは確かではありません)、変数、スタックされた場合でも、ヒープに続い持ち上げます。

しかし、なぜなら、あなたはevent.WaitOne()の後ろにあるすべてのものを(event.Setが呼び出されたブロックである)継続の中に入れないのですか?私はこれがこの種の状況を処理するための推奨された方法であるべきだと思います。あなたはこのようにトラブルに遭遇することはありません(あなたは外側のブロックにBarを必要とせず、依然としてMREを使って確認できます)。

これをTaskオブジェクトを使用してOperationsにすることを検討します。これは、このすべてを一度に解決します(たとえば、AsyncOperationからTaskを返すなど)。

var task = foo.AsyncOperation(Taks.Factory.StartNew(() => { /* create and return a bar */ })); 
var theBar = task.Result; // <- this will wait for the task to finish 
// use your bar 

がPS:閉鎖は基本的にはクラスにそれらをラップするあなたは、[タスクの結果を待って、返されたバーを使用して...

class Foo 
{ 
// ... 
private Task<Bar> AsyncOperation(Task<Bar> initializeBar) 
{ 
    return initializeBar 
      .ContinueWith(
      bar => { 
        /* do your work with bar and return some new or same bar */ 
        return bar; 
        }); 
} 
} 

をし、このようにそれを使用することができます-OBject;) PPS:AsyncOperationを使わずにこのコードをテストするのは難しいですが、間違ったスペルや入力によってモジュロ構文エラーが発生するでしょう

+0

私はそれほど確かではありません - どのように*保証*働くのですか?通常のデータ/注文ルールは、シングルスレッドのときにのみ適用されます... –

+0

明らかに、私が行うよりも、これらのことについてもっと多くのことを知っています - このコードをある種のレジスタに入れて再利用する/他のスレッドで再作成)と私は思う*これは必ず起こりません*。そして、私は、F#が使用する非同期のものと正直言って、非常によく似たものを見たと思う:私は、類似したものを使用する(ManualResetEventsを使う - スレッドを同期させるために局所的に定義されている)しかし、私はコンシューシー・ウィザードではありません。問題がどこにあるのかを知る代わりに、問題がどこにあるのかを指摘できますか? (実際に興味がない) – Carsten

+0

ああ待っている - あなたは(もちろん)バーオブジェクトを考えているのだろうか...うーん、本当に問題を綴るかもしれない... – Carsten