web-dev-qa-db-ja.com

POSIXスレッドを使用してCでスレッド固有のグローバル変数を作成するにはどうすればよいですか?

私はPOSIXスレッドについて学び、スレッド固有のデータのセクションに来ました。この本は、ファイル記述子を使用した優れた例を示しています。ただし、今回はグローバル変数を使用することを除いて、同じ例を自分で実行したかったのです。しかし、私はこの概念を完全に理解するのに少し苦労しています。

私がやりたいことは次のとおりです。

  • グローバル整数を作成する
  • グローバルintのキーを宣言します

主に:

  • グローバル整数を値に設定します。 10
  • クリーンアップせずにキーを作成します
  • 4つのスレッドを作成し、それらを送信してthread_funcを実行します
  • スレッドはそのコピーのみを参照するため、値がまだ10であるかどうかを確認します

thread_func内:

  • pthread_setspecific(key、global variable)を使用してローカルインスタンスを作成します-これを正しく解釈しているかどうかはわかりません
  • 関数を呼び出す-dosomething()
  • 出口

do_somethingで

  • ローカルポインタを作成し、それをpthread_getspecific(key)に割り当てます-これにより、グローバル変数のスレッド固有のバージョンが取得されます
  • ローカルポインタに格納されている値を2に変更します
  • 出口

コードは次のとおりです。

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>

#define NUMTHREADS 4

pthread_key_t glob_var_key;
int glob_var;

void do_something()
{
    //get thread specific data
    int* glob_spec_var = (int*) pthread_getspecific(glob_var_key);
    printf("Thread %d glob_spec before mod value is %d\n", (unsigned int) pthread_self(), *glob_spec_var);
    *glob_spec_var = 2;
    printf("Thread %d glob_spec after mod value is %d\n", (unsigned int) pthread_self(), *glob_spec_var);
}

void* thread_func(void *arg)
{
    pthread_setspecific(glob_var_key, &glob_var);
    do_something();
    pthread_exit(NULL);
}

int main(void)
{
    pthread_t threads[NUMTHREADS];
    int i;
    glob_var = 10;
    pthread_key_create(&glob_var_key,NULL);
    printf("Main: glob_var is %d\n", glob_var);
    for (i=0; i < NUMTHREADS; i++)
    {
        pthread_create(&threads[i],NULL,thread_func,NULL);
    }

    for (i=0; i < NUMTHREADS; i++)
    {
        pthread_join(threads[i], NULL);
    }
    printf("Main: glob_var is %d\n", glob_var);

    return 0;
}

私が理解したことから、pthread_getspecificを呼び出すと、各スレッドはメモリアドレス用に独自の一意のメモリアドレスを持っているはずです-ここではそうではありませんでした。私はこれについて正しく行っていないことを知っています。getspecificを実行するときに各スレッドのメモリアドレスを調べようとすると、同じメモリアドレスが表示されました。おそらく誰かが私に、(ファイル記述子ではなく)グローバル変数を使用し、スレッドがそれをローカル変数と見なすスレッド固有の使用法がある例を指摘することができます。

13
Cal

TLS(スレッドローカルストレージ)の目的は、コードがすべてのスレッドで既知の共有キーによってアクセスされるデータベースに格納されているスレッド固有のデータを取得できるようにする定義済みのメカニズムを提供することです。あなたのコードは同じデータをTLSに保存しています:単一のグローバル変数のアドレス)。したがって、スレッドがtls-keyを使用してこのデータを要求すると、それらはすべて同じアドレスを返します。

私はあなたのコードがこのようなことをするつもりだと思います:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

#define NUMTHREADS 4

pthread_key_t glob_var_key;

void do_something()
{
    //get thread specific data
    int* glob_spec_var = pthread_getspecific(glob_var_key);
    printf("Thread %d before mod value is %d\n", (unsigned int) pthread_self(), *glob_spec_var);
    *glob_spec_var += 1;
    printf("Thread %d after mod value is %d\n", (unsigned int) pthread_self(), *glob_spec_var);
}

void* thread_func(void *arg)
{
    int *p = malloc(sizeof(int));
    *p = 1;
    pthread_setspecific(glob_var_key, p);
    do_something();
    do_something();
    pthread_setspecific(glob_var_key, NULL);
    free(p);
    pthread_exit(NULL);
}

int main(void)
{
    pthread_t threads[NUMTHREADS];
    int i;

    pthread_key_create(&glob_var_key,NULL);
    for (i=0; i < NUMTHREADS; i++)
        pthread_create(threads+i,NULL,thread_func,NULL);

    for (i=0; i < NUMTHREADS; i++)
        pthread_join(threads[i], NULL);

    return 0;
}

出力

Thread 2625536 before mod value is 1
Thread 741376 before mod value is 1
Thread 3162112 before mod value is 1
Thread 3698688 before mod value is 1
Thread 2625536 after mod value is 2
Thread 741376 after mod value is 2
Thread 3162112 after mod value is 2
Thread 3698688 after mod value is 2
Thread 2625536 before mod value is 2
Thread 741376 before mod value is 2
Thread 3162112 before mod value is 2
Thread 3698688 before mod value is 2
Thread 2625536 after mod value is 3
Thread 741376 after mod value is 3
Thread 3162112 after mod value is 3
Thread 3698688 after mod value is 3
16
WhozCraig

これは答えではありませんが、補足事項です。

Linux固有のコードで作業している場合は、__threadキーワードを使用できます。基本的に、

static __thread int counter = 5;

スレッドごとに異なるcounter変数を作成し、新しいスレッドが作成されるたびに値5に初期化されます。 C11は_Thread_localキーワードを使用して同じセマンティクスを標準化したため、このようなコードはC11と将来互換性があります。これは、C99以降を宣言したアーキテクチャを除くCを使用するすべてのアーキテクチャで、POSIXスレッド固有の関数(実装固有の制限があり、__threadキーワードと比較して非常に面倒です)よりもはるかに賢明です。 標準の非無償 "(つまり、Microsoft)。

詳細については、 スレッドローカルストレージの章 in GCCドキュメント を参照してください。

18
Nominal Animal

一般的に、あなたが探しているのは「スレッドローカルストレージ」です。ここにいくつかのリンクがあります:

pthread_getspecific()の選択は正しいです。これが良い例です:

上記の例を確認してください。問題を指摘するか、適切な代替案を提案すると思います。

私見では...

4
paulsm4