0

構造体を介して呼び出された関数に複数のパラメーターを渡す単純なプログラムを pthreads で作成しました。次の 2 つのプログラムについて考えてみましょう。
プログラム 1 :

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

struct args{
    long long val1;
    int val2;
};

void *hello(void* threadid){
    struct args *tid;
    tid=(struct args*)threadid;
    printf("thread %lld\n",tid->val1);
    pthread_exit(NULL);
}

int main(){
    pthread_t threads[20];
    int i;
    for(i=0;i<20;i++){
        // ***** specific memory given to the struct *****
        struct args* a1=(struct args*)malloc(sizeof(struct args));    
        a1->val1=i;
        a1->val2=i+1;
        int ret=pthread_create(&threads[i],NULL,hello,(void*)a1);
        if(ret){
            printf("error code %d for %d\n",ret,i);
        }
    }
    pthread_exit(NULL);
}

期待どおりに出力を印刷し、いくつかの順列0..19

一方、プログラム p2 を検討してください。

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

struct args{
    long long val1;
    int val2;
};

void *hello(void* threadid){
    struct args *tid;
    tid=(struct args*)threadid;
    printf("thread %lld\n",tid->val1);
    pthread_exit(NULL);
}

int main(){
    pthread_t threads[20];
    int i;
    for(i=0;i<20;i++){
        // ***** struct made like normal declarations *****
        struct args a1;
        a1.val1=i;
        a1.val2=i+1;
        int ret=pthread_create(&threads[i],NULL,hello,(void*)&a1);
        if(ret){
            printf("error code %d for %d\n",ret,i);
        }
    }
    pthread_exit(NULL);
}

このプログラムには、次のような繰り返しの不完全なエントリがあります

thread 3
thread 5
thread 3
thread 4
thread 3
thread 6
thread 7
thread 8
thread 9
thread 10
thread 11
thread 12
thread 13
thread 15
thread 15
thread 16
thread 17
thread 18
thread 19
thread 19

構造体のインスタンス化がこの種のオーバーラップを直接引き起こすのはなぜですか? C はループのたびに新しいメモリ ブロックを提供するべきではありませんか?

4

3 に答える 3

3

2番目の例では、ループa1内の自動保存期間で宣言されています。forこれは、すべての反復の最後に、そのストレージの場所次の反復で再利用される可能性があることを意味します。したがって:

int ret=pthread_create(&threads[i],NULL,hello,(void*)&a1);

...後続の反復で変更されるメモリ位置にアドレス ( ) を渡す場合があります。一方、反復ごとに異なるメモリ位置にポインターを割り当てます。&a1malloc

スレッドが次の反復の開始前または開始後に実行されるかどうかは保証されないため、上記のコードを実行するたびに、実際には異なる結果が得られる可能性があります。

さらに、mallocポインタをどこにも格納せずに使用したり、スレッドで解放したりすると、メモリ リークが発生します。すべてのスレッドが終了する前に構造体が範囲外になることもあり、その結果、スレッドはダングリング ポインターにアクセスします。最後に、どのスレッドにも参加していないため、プログラムが終了する前に実際に完全に実行されるスレッドの数はわかりません。

マルチスレッドの素晴らしい世界へようこそ。

于 2013-02-12T16:12:57.873 に答える
2

C は、ループ内で宣言された変数に新しいメモリ ブロックを提供しない場合があります。同じメモリを使用することができます。重複した出力が表示される理由は、ほとんどの場合、同じメモリ領域がすべてのスレッドに渡され、メイン スレッドでも同時に更新されるためです。新しい変数を宣言しても、新しい変数が新しいメモリ ブロックを取得する必要はありません。メモリブロックは再利用できます。これは予測できません。

また、このようなコーディングは危険です。ループが終了すると、変数がスコープ外になり、スレッドの 1 つが変数にアクセスしようとしたときに未定義の動作が発生する可能性があるからです。

同じメモリが割り当てられているかどうかを確認するには、各スレッドでスレッドに渡す a1 のアドレスを出力してみてください。これにより、最高の透明度が得られます。

于 2013-02-12T16:14:22.203 に答える
2

2 番目の例a1では、ループの反復ごとに for の新しいインスタンスを取得しますが、これは以前のすべての反復で使用された同じメモリを指す可能性が非常に高くなります。

a1スレッドがいつ、どのくらいの期間スケジュールされるかは保証されていないため、実行時に更新されるかどうか (およびその頻度) を知ることはできませんhello。これは、出力される値が [0, 20) の範囲内になることを意味しますが、それ以外の場合は予測できません。

于 2013-02-12T16:10:17.347 に答える