web-dev-qa-db-ja.com

C ++:マルチスレッドプログラムの静的変数

マルチスレッドプログラムで静的変数(特に関数内)を使用する場合の問題は何ですか?

ありがとう。

30
user542687

説明に役立つ例をランダムに選択するには、Cライブラリの asctime のようなインターフェイスを使用します。プロトタイプは次のようになります。

_ char *
 asctime(const struct tm *timeptr);
_

これには、返された_char*_に文字を格納するためのグローバルバッファーが暗黙的に必要です。これを実現する最も一般的で簡単な方法は次のようなものです。

_ char *
 asctime(const struct tm *timeptr)
 {
    static char buf[MAX_SIZE];

    /* TODO: convert timeptr into string */

    return buf;
 }
_

bufasctime()の呼び出しごとに同じアドレスにあるため、これはマルチスレッド環境では完全に壊れています。 2つのスレッドが同時にasctime()を呼び出すと、互いの結果が上書きされるリスクがあります。 asctime()のコントラクトで暗黙的に示されているのは、文字列の文字がasctime()の次の呼び出しまで固定され、同時呼び出しがこれを中断することです。

この特定の例では、スレッドローカルストレージ(___thread_、__declspec(thread))を介してこの特定の問題を回避するいくつかの言語拡張があります。このアイデアが_thread_local_キーワードとしてC++ 0xに組み込まれたと思います。

それでも、グローバル変数を使用するのが悪い理由と同様の理由で、このように使用するのは悪い設計上の決定だと主張します。特に、呼び出し先ではなく、呼び出し元がこの種の状態を維持および提供するためのよりクリーンなインターフェイスと考えることができます。ただし、これらは主観的な議論です。

21
asveikau

初期化はスレッドセーフではありません。 2つのスレッドが関数に入ることができ、どちらも関数スコープの静的変数を初期化できます。それは良いことではありません。結果がどうなるかわからない。

C++ 0xでは、関数スコープの静的変数の初期化はスレッドセーフになります。関数を呼び出す最初のスレッドは変数を初期化し、その関数を呼び出す他のスレッドは、その初期化が完了するまでブロックする必要があります。

現在、C++ 0x同時実行メモリモデルとスレッドサポートおよびアトミックライブラリを完全に実装するコンパイラ+標準ライブラリのペアはないと思います。

28
James McNellis

静的変数は通常、関数の複数の呼び出しが状態を共有し、相互に干渉することを意味します。

通常、関数は自己完結型にする必要があります。作業するすべてのローカルコピーを持ち、外界バーパラメータと戻り値と何も共有しません。 (とにかく、あなたが特定の方法を考えれば、それは関数の一部ではありません。)

考慮してください:

int add(int x, int y);

間違いなくスレッドセーフなxとyのローカルコピー。

void print(const char *text, Printer *printer);

危険、外部の誰かが同じプリンターで何かをしているかもしれません。その上で別のprint()を呼び出す。

void print(const char *text);

間違いなくスレッドセーフではなく、2つの並列呼び出しが同じプリンターを使用することが保証されています。

もちろん、共有リソースへのアクセスを保護する方法があります(検索キーワード:mutex);これがまさにあなたの直感です。

変数への非同期の並列書き込みも、読み取りと書き込みと同様に、ほとんどの場合スレッドセーフではありません。 (検索キーワード:synchronizationsynchronization primitives [mutexは1つだけ]]、またatomicity/atomic operation並列アクセスが安全な場合。)

2
aib