5

問題は次のとおりです。

10 個のスレッドを作成し、それぞれがポインターによってスレッド関数に渡されるトレッド "id" を出力する短いプログラムを作成したいと考えています。

プログラムの完全なコードは次のとおりです。

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

struct params {
        pthread_mutex_t mutex;
        int id;
};

typedef struct params params_t;

void* hello(void* arg){
    int id;
    pthread_mutex_lock(&(*(params_t*)(arg)).mutex);
    id = (*(params_t*)(arg)).id;
    pthread_mutex_unlock(&(*(params_t*)(arg)).mutex);
    printf("Hello from %d\n", id);
}


int main() {
    pthread_t threads[10];
    params_t params;
    pthread_mutex_init (&params.mutex , NULL);

    int i;
    for(i = 0; i < 10; i++) {
            params.id = i;
            if(pthread_create(&threads[i], NULL, hello, &params));
    }

    for(i = 0; i < 10; i++) {
            pthread_join(threads[i], NULL);
    }

    return 0;
}

想定される出力は次のとおりです (この順序では必要ありません)。

Hello from 0
....
Hello from 9

実際の結果は次のとおりです。

Hello from 2
Hello from 3
Hello from 3
Hello from 4
Hello from 5
Hello from 6
Hello from 8
Hello from 9
Hello from 9
Hello from 9

関数内のさまざまな場所にミューテックスを配置しようとしましたhello()が、役に立ちませんでした。

スレッド同期を実装するにはどうすればよいですか?

編集:結果は必要ではないと仮定すると、0...9 これらの数字の任意の組み合わせにすることができますが、それぞれが 1 回だけ表示される必要があります。

4

5 に答える 5

8

問題は以下のコードにあります。

for(i = 0; i < 10; i++) 
{             
  params.id = i;             
 if(pthread_create(&threads[i], NULL, hello, &params));     
} 

params.id 値はメイン スレッドで更新され続けますが、すべてのスレッドに同じポインターを渡します。

問題を解決するには、動的に割り当てることによって params 用に別のメモリを作成し、それを別のスレッドに渡してください。

EDIT1: 保護するためのミューテックスの使用も間違った考えです。ID の設定中にメインでミューテックスを使用すると、更新が相互に排他的になる可能性がありますが、目的の出力が得られない場合があります。異なるスレッドで 0 から 9 の値を取得する代わりに、すべて 9 を取得するか、複数のスレッドが同じ値を出力する可能性があります。

したがって、スレッド同期を使用することは、期待する出力にはあまり適していません。すべてのスレッド間で 1 つの param 変数を使用し、各スレッドから 0 から 9 として出力を取得する必要がある場合は、pthread_join を最初のループに移動することをお勧めします。これにより、各スレッドが作成され、値が出力され、メインが次のスレッドを生成する前に返されることが保証されます。この場合、ミューテックスも必要ありません。

EDIT2: 更新された質問については、数字 0..9 を連続して印刷する必要がないことを尋ねられた場合、印刷はランダムにすることができますが、一度だけ、問題は多かれ少なかれ同じままです。

ここで、params.id の値が最初に 0 で、スレッド 0 が作成されたとします。スレッド 0 は、メイン スレッドで更新される前にそれを出力する必要があります。それ以外の場合、スレッド 0 がそれにアクセスすると、params.id の値1 になっていて、独自の値のセットを取得することはできません。したがって、スレッド 0 がメインで更新される前にそれを確実に出力するには、次の 2 つの方法があります。

  • メインが値を更新する前に、スレッド 0 が実行と印刷を完了していることを確認してください
  • 条件変数とシグナリングを使用して、メイン スレッドが値を更新する前にスレッド 0 が印刷を完了するのを待機するようにします (詳細については、以下の Arjun の回答を参照してください)。

私の正直な意見では、同期と共有メモリを学習するために間違った問題を選択しました。「Producer-Consumer」のようないくつかの優れた問題でこれを試すことができます。ここでは、物事が機能するために同期が本当に必要です。

于 2012-06-04T10:00:15.393 に答える
2

2 つの問題があります。

A. を使用してlockいますmainが、このロックを認識していません。

B.lockこの場合、A では不十分です。あなたが望むのは、スレッドが互いにシグナルを送ることによって協力することです(スレッドが印刷が完了したと言うまで変数をインクリメントしmainたくないため)。pthread_cond_tこれを実現するためにa を使用できます(詳細については、こちらを参照してください)。これは次のコードに要約されます (基本的に、コードに の適切な使用法pthread_cond_tと、何が起こっているかを説明するコメントを追加しました):

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

struct params {
        pthread_mutex_t mutex;
        pthread_cond_t done;
        int id;
};

typedef struct params params_t;

void* hello(void* arg){

    int id;
    /* Lock.  */
    pthread_mutex_lock(&(*(params_t*)(arg)).mutex);

    /* Work.  */
    id = (*(params_t*)(arg)).id;
    printf("Hello from %d\n", id);

    /* Unlock and signal completion.  */
    pthread_mutex_unlock(&(*(params_t*)(arg)).mutex);
    pthread_cond_signal (&(*(params_t*)(arg)).done);

    /* After signalling `main`, the thread could actually
    go on to do more work in parallel.  */
}


int main() {

    pthread_t threads[10];
    params_t params;
    pthread_mutex_init (&params.mutex , NULL);
    pthread_cond_init (&params.done, NULL);

    /* Obtain a lock on the parameter.  */
    pthread_mutex_lock (&params.mutex);

    int i;
    for(i = 0; i < 10; i++) {

            /* Change the parameter (I own it).  */    
            params.id = i;

            /* Spawn a thread.  */
            pthread_create(&threads[i], NULL, hello, &params);

            /* Give up the lock, wait till thread is 'done',
            then reacquire the lock.  */
            pthread_cond_wait (&params.done, &params.mutex);
    }

    for(i = 0; i < 10; i++) {
            pthread_join(threads[i], NULL);
    }

    /* Destroy all synchronization primitives.  */    
    pthread_mutex_destroy (&params.mutex);
    pthread_cond_destroy (&params.done);

    return 0;
}

あなたが試みている例は、おそらくPOSIXスレッドライブラリについて学ぶためのおもちゃのプログラムだと思います。現実の世界では、私たち全員が知っているように、これはスレッドを使用しなくてもはるかに高速に実行できます。しかし、あなたはすでにこれを知っています。

于 2012-06-04T10:17:55.417 に答える
1

The problem is that you are modifying the params.id "unprotected" in main. This modification in main also needs to be mutex protected. You could protect this access by localizing this by creating getId() and setId() functions that would lock the mutex and protect access to the id, as follows. This will most likely still give the problem reported, since depending on when the thread calls getData() it will have one value or another. So to solve this, you could add an incrementId() function and call it from the hello() function.

struct params {
        pthread_mutex_t mutex;
        int id;
};

typedef struct params params_t;

int getId(params_t *p)
{
    int id;
    pthread_mutex_lock(&(p->mutex));
    id = p->id;
    pthread_mutex_unlock(&(p->mutex));

    return id;

}

void setId(params_t *p, int val)
{
    pthread_mutex_lock(&(p->mutex));
    p->id = val;
    pthread_mutex_unlock(&(p->mutex));
}

void incrementId(params_t *p)
{
    pthread_mutex_lock(&(p->mutex));
    p->id++;
    pthread_mutex_unlock(&(p->mutex));
}

void* hello(void* arg){
    params_t *p = (params_t*)(arg);
    incrementId(p);
    int id = getId(p);

    // This could possibly be quite messy since it
    // could print the data for multiple threads at once
    printf("Hello from %d\n", id);
}


int main() {
    pthread_t threads[10];
    params_t params;
    params.id = 0;
    pthread_mutex_init (&params.mutex , NULL);

    int i;
    for(i = 0; i < 10; i++) {
            if(pthread_create(&threads[i], NULL, hello, &params));
    }

    for(i = 0; i < 10; i++) {
            pthread_join(threads[i], NULL);
    }

    return 0;
}

A better way to get a unique thread id would be to define the hello method as follows:

void* hello(void* arg){
    pthread_t threadId = pthread_self();
    printf("Hello from %d\n", threadId);
}

And to avoid the problem with all threads trying to print at once, you could do the following:

void* hello(void* arg){
    params_t *p = (params_t*)(arg);
    pthread_mutex_lock(&(p->mutex));

    p->id++;
    int id = p->id;
    printf("Hello from %d\n", id);

    pthread_mutex_unlock(&(p->mutex));
}
于 2012-06-04T09:58:32.217 に答える
0

目的の出力を取得する最も簡単な方法は、main関数を次のように変更することです。

int main() {
    pthread_t threads[10];
    params_t params;
    pthread_mutex_init (&params.mutex , NULL);

    int i;
    for(i = 0; i < 10; i++) {
            params.id = i;
            if(pthread_create(&threads[i], NULL, hello, &params));
            pthread_join(threads[i], NULL); //wait for thread to finish
    }

    /*for(i = 0; i < 10; i++) {
            pthread_join(threads[i], NULL);
    }*/

    return 0;
}

出力は次のようになります。

Hello from 0
...
Hello from 9

編集:修正された質問の同期は次のとおりです。

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

struct params {
    pthread_mutex_t* mutex;
    int id;
};

typedef struct params params_t;

void* hello(void* arg){
    int id = 0;
    params_t* params = (params_t*)arg;
    if(params != 0)
    {
        id = params->id;
        delete params;
        params = 0;
    }
    printf("Hello from %d\n", id);
}


int main() {
    pthread_t threads[10];
    params_t* params = 0;
    pthread_mutex_t main_mutex;
    pthread_mutex_init (&main_mutex , NULL);

    int i;
    for(i = 0; i < 10; i++) {
        params = new params_t(); //create copy of the id to pass to each thread -> each thread will have it's own copy of the id
        params->id = i;
        params->mutex = &main_mutex;
        if(pthread_create(&threads[i], NULL, hello, params));
    }

    for(i = 0; i < 10; i++) {
        pthread_join(threads[i], NULL);
    }

    return 0;
}

印刷される前に他のスレッドが id を変更しないように、各スレッドには独自の id のコピーが必要です。

于 2012-06-04T10:50:37.477 に答える