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スレッドの両方で機能する1つまたは複数のソリューションが必要です。

編集:この機能が必要な理由は、シングルスレッドまたはマルチスレッド(デバッグ目的)のコードを実行するときに違いがあるかどうかをテストするための邪魔にならない方法を提供するためです。

4

4 に答える 4

3

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

int deepthought()
{
    return 42;
}

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

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

PS:void *不透明なポインタを取得するためにを使用しないことをお勧めします。代わりに、struct Lock型安全性のためにプラットフォーム固有の1つの要素を実装します。

于 2011-01-22T12:37:26.673 に答える
3

1つのアプローチは、グローバルロックを使用して初期化パスをシリアル化することです。ただし、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
    // ....
}

そうは言っても、データに作用する関数ではなく、データにロックをかけることをお勧めします。結局のところ、このようなリーダーをどのように同期させるつもりですか?後で別々のアレイに別々のロックを使用したい場合はどうなりますか?そして、後でそのロックを取得する他の関数を作成したい場合はどうでしょうか。

于 2011-01-22T12:50:04.047 に答える
2

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

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

于 2011-01-22T13:18:20.357 に答える
1

さまざまなオプションがあります。

  • 安全なタイミングで(たとえば、コードを追加のスレッドに公開する前に)初期化コードを手動で呼び出します。
  • 使用pthread_once()または同等のもの。これにより、初期化コードが1回呼び出され、後続のすべての呼び出し元がその効果を確認できます。
  • 関数の呼び出しを伴わないロックの静的初期化を使用します。たとえば、pthreadを使用します

    static pthread_mutex_t mylock = PTHREAD_MUTEX_INITIALIZER;
    
于 2011-01-22T18:55:42.767 に答える