2011-01-22 26 views
3

私はこのようなものにするために関数内でstaticキーワードをうまく利用して、最大考えた:要するにマルチスレッドコンテキストで静的変数を初期化する方法は?

void threadSafeWrite(int *array, int writeIndex, int writeData){ 
    static void *threadLock = Lock_create(); //in my code locks are void* to be cross-platform compatable 
    Lock_aquire(threadLock); 
    array[writeIndex] = writeData; 
    Lock_release(threadLock); 
} 

それがクリティカルセクションを作るために良い方法のように思えます。私の質問は、スレッドセーフな方法でthreadlockをどのように初期化するのですか?私が恐れる例の問題は、ロックが複数回割り当てられ、各スレッドが異なるロックを使用するということです。どのようにこれを修正するための任意のアイデア?鶏と卵の問題のようだ。私は、pthreadとwindowsスレッドの両方で動作する解決策(または解決策)が必要です。

EDIT:私がこの機能を望む理由は、デバッグ目的のためにシングルスレッドまたはマルチスレッドのコードを実行するときに違いがあるかどうかをテストする非侵入的な方法を提供するからです。

答えて

3

グローバルロックを使用して初期化パスをシリアル化する方法があります。しかし、SMPメモリバリア上に移植可能なラッパーが必要です。原則として、ロック取得前のメモリ読み出しの結果をコンパイラおよび/またはCPUがキャッシュすることができるので、ロックによって暗示される取得バリアはではなく、で十分です。これは例です:

Lock global_init_lock; // Should have low contention, as it's only used during startup 

void somefunc() { 
    static void *data; 
    static long init_flag = 0; 
    if (!init_flag) { // fast non-atomic compare for the fast path 
     global_init_lock.Lock(); 
     read_memory_barrier(); // make sure we re-read init_flag 
     if (!init_flag) 
      data = init_data(); 
     write_memory_barrier(); // make sure data gets committed and is visible to other procs 
     init_flag = 1; 
     global_init_lock.Unlock(); 
    } 
    read_memory_barrier(); // we've seen init_flag = 1, now make sure data is visible 
    // .... 
} 

つまり、データに作用する機能ではなく、データをロックすることをおすすめします結局のところ、このような読者をどのように同期させるつもりですか?後で別々の配列に別々のロックを使用する場合はどうすればよいでしょうか?後でそのロックを取る他の機能を書き留めたいのであれば、どうしたらいいでしょうか?

+0

read_memory_barrierは、ロックによって暗示されているので必須ではありません。しかし、これは二重チェックされたロックイディオムであり、ほとんどのメモリモデルでは無効です。 2番目のスレッドは 'init_flag = 1'を参照するかもしれませんが、' data'やそれが指しているものの古い値があります。 – jilles

+0

@jilles「ほとんどのメモリモデル」について私は知らない。 –

+0

あなたはおそらくinit_dataを何回も呼び出す気にせず、その最終的な出来事で何かを捨てる準備ができていれば、CASでこれを行うことができ、ロックを避けることができます。 –

3

static変数の初期化子はCで定数でなければならないため、機能しません。関数呼び出しは定数ではありません。これは、mainが入力される前にstaticが作業を実行できるC++とは異なります。たとえば、これはコンパイルされません。

int deepthought() 
{ 
    return 42; 
} 

void ask() 
{ 
    static int answer = deepthought(); 
} 

最も単純なオプションは、ロックをグローバルにしてからMTモードに入る前に初期化することです。より良い選択肢は、ガードされたデータと一緒にそれらを渡すことです。

P.S .: void *を使用して不透明なポインタを取得することをお勧めします。代わりに、タイプセーフティのためにプラットフォーム固有の1つの要素struct Lockを実装します。

+0

表情は、イムが使用してvoid *型の良いアドバイスが答えます。 – chacham15

2

Lock_createの全体のアイデアは壊れています。それを作成する行為には同期が必要ですが、ロックがまだないため保証できません。ロックのためにポインタを使用しないでください。代わりに、構造体を作成し、その構造体のアドレスをロックおよびロック解除関数に渡します。あるいは、あなたのOSで提供されているものを使用することをお勧めします。

ロックを「グローバル」にする必要があると言われた人はすべて間違っています。機能レベルスコープは静的な記憶期間であれば問題ありません。割り当て関数Lock_createを削除して、ロックに適切なタイプを使用するだけです。

0

は、さまざまなオプションがある:

  • 手動で(例えば、追加のスレッドにコードを露光する前に)安全な時に初期化コードを呼び出します。
  • pthread_once()またはこれに相当するものを使用すると、初期化コードが一度呼び出され、後続の呼び出し元すべてがその効果を確認できます。
  • 関数の呼び出しに関係しないロックの静的初期化を使用します。例えば、pthreadsを使用してbdonlansで

    static pthread_mutex_t mylock = PTHREAD_MUTEX_INITIALIZER; 
    
関連する問題