3

次の試験に備えて、以前の試験でオペレーティング システムの教授が示した問題を解こうとしています。

問題は、2 つのスレッドが同時に実行され、異なる時間で完了する可能性があることです。特定のスレッドが完了した後、他のスレッドが完了するまでブロックする必要があります。その後、実行を継続できます。

概念的には単純に思えますが、私のコードは思った通りに動作しません。

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

#define N 10

sem_t t_1_sem;
sem_t t_2_sem;

void *thread(void *vargp);
/* shared by both threads*/
struct {
    int count;
} thread_count;

int main() {
    pthread_t tid, tid1;

    thread_count.count = 0;

    sem_init(&t_1_sem, 0, 1);
    sem_init(&t_2_sem, 0, 1);
    printf("Hello from main thread! tid:%ld pid:%d\n", pthread_self(), getpid());
    pthread_create(&tid, NULL, thread, NULL);
    pthread_create(&tid1, NULL, thread, NULL);

    pthread_join(tid, NULL);
    pthread_join(tid1, NULL);

    exit(0);
}

void *thread(void *vargp) {
    int i, tid;

    int val, val2;
    sem_getvalue(&t_1_sem, &val);
    sem_getvalue(&t_2_sem, &val2);
    printf("initial value::: %d : %d\n", val, val2);


    tid = thread_count.count;
    thread_count.count += 1;

    for(i = 0;i<N;i++){
        printf("%d, %d\n", tid, i);
        fflush(stdout);
        //sleep(0.1);
    }

    // TODO
    // barrier
    sem_getvalue(&t_1_sem, &val);
    sem_getvalue(&t_2_sem, &val2);
    printf("second value::: %d : %d\n", val, val2);
    int sem_val;
    if(tid == 0){
        // free other
        sem_getvalue(&t_1_sem, &sem_val);
        printf("posting to 2, waiting on 1 w/ %d count\n", sem_val);
        sem_post(&t_2_sem);
        // wait on this one
        sem_wait(&t_1_sem);
        printf("done waiting on 1\n");
    } else if(tid == 1){
        sem_getvalue(&t_2_sem, &sem_val);
        printf("posting to 1, waiting on 2 w/ %d count\n", sem_val);
        sem_post(&t_1_sem);
        sem_wait(&t_2_sem);
        printf("done waiting on 2\n");
    }

    sem_getvalue(&t_1_sem, &val);
    sem_getvalue(&t_2_sem, &val2);
    printf("final value::: %d : %d\n", val, val2);
    return NULL;
}

私が期待しているのは、両方のスレッドが 10 までカウントし、次に 2 つの「最終値」printfが隣り合って発生していることです。ただし、私が見ているのは、スレッドが 10 までのカウントを終了した直後に発生する「最終値」の出力です。待機していないようです。

sem_valまた、「posting to N」で出力する整数に対して本当に奇妙な値を取得していますprintf。たとえば、次のようになります。

Hello from main thread! tid:-1606277344 pid:5479
initial value::: 0 : 0
0, 0
initial value::: 0 : 0
1, 0
0, 1
1, 1
0, 2
1, 2
1, 3
1, 4
1, 5
0, 3
1, 6
0, 4
1, 7
0, 5
1, 8
0, 6
1, 9
0, 7
second value::: 0 : 0
posting to 1, waiting on 2 w/ -1809628646 count
0, 8
done waiting on 2
final value::: 0 : 0
0, 9
second value::: 0 : 0
posting to 2, waiting on 1 w/ -1809628646 count
done waiting on 1
final value::: 0 : 0

アイデア/ヒントはありますか?

4

7 に答える 7

7

The Little Book of Semaphoresを参照してください。セクション 3.5 では、Barrier パターンとそれを正しく実装する方法について説明します。

それがあなたの質問に直接答えないことは知っていますが、正しい方向に向ける必要があります。

于 2009-10-15T01:49:56.267 に答える
6

sem_getvalue() は osx では実装されていません。 http://discussions.apple.com/thread.jspa?messageID=9404131&tstart=0

于 2009-10-15T12:59:46.333 に答える
1

これは、あなたの望むことですか?

0, 0
0, 1  
0, 2
0, 3
0, 4
0, 5
0, 6
0, 7
0, 8
0, 9
1, 0
1, 1
1, 2
1, 3
1, 4
1, 5
1, 6
1, 7
1, 8
1, 9

私はあなたの考えを理解しているかどうかわかりませんが、セマフォを間違って使用している可能性があります. 以下は、上記の現象を生成するコードです。うまくいけば、それはあなたの問題に役立つ何かを持っています.

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

#define N 10

sem_t t_1_sem;
sem_t t_2_sem;

void *thread_1(void *vargp);
void *thread_2(void *vargp);
/* shared by both threads*/
struct {
    int count;
} thread_count;

 int main() {
pthread_t tid, tid1;

thread_count.count = 0;

sem_init(&t_1_sem, 0, 1);
sem_init(&t_2_sem, 0, 1);
printf("Hello from main thread! tid:%ld pid:%d\n", pthread_self(), getpid());
pthread_create(&tid, NULL, thread_1, NULL);
pthread_create(&tid1, NULL, thread_2, NULL);

pthread_join(tid, NULL);
pthread_join(tid1, NULL);

exit(0);
}

void *thread_1(void *vargp) {
int i, tid;

int val, val2;
sem_getvalue(&t_1_sem, &val);
printf("enter thread_1\n");
sem_wait(&t_1_sem);
//even thread_1 is sleeping , thread_2 will not be scheduled to run
//as thread_1 is holding the semphore
sleep(1);
tid = thread_count.count;
thread_count.count += 1;

for(i = 0;i<N;i++){
    printf("%d, %d\n", tid, i);
    fflush(stdout);
    //sleep(0.1);
}

    sem_post(&t_1_sem);
    return NULL;
}


void *thread_2(void *vargp) {
int i, tid;

int val, val2;
sem_getvalue(&t_1_sem, &val);

printf("enter thread_2\n");
sem_wait(&t_1_sem);
tid = thread_count.count;
thread_count.count += 1;

for(i = 0;i<N;i++){
    printf("%d, %d\n", tid, i);
    fflush(stdout);
    //sleep(0.1);
}

    sem_post(&t_1_sem);
    return NULL;
}
于 2009-10-15T02:21:00.097 に答える
1

ミューテックスと条件変数を備えた pthreads を使用して、このような問題にうまく対処しました。

セマフォは、一般的なスレッド間またはプロセス間のメッセージング メカニズムであり、スレッドの調整に偶然使用できますが、多少の困難があります。ミューテックスは、スレッドの調整を直接目的とした特殊なバイナリ セマフォであり、それを行うための簡素化された API を提供します。

したがって、セマフォの使用に制約されていない場合は、mutex を使用する方が簡単に作業を完了できると考えることができます。

どちらのスレッド調整アプローチを選択する場合でも、検索エンジンを使用して、同様の問題を解決するコード サンプルを見つけ、それらを理解していることを確認してください。たとえば、「pthread セマフォの例」と「pthread ミューテックスの例」は、どちらも興味深いヒットがたくさんありました。

大きなヒント: 実際に 2 つのセマフォが必要であることを確認してください。1 つのセマフォまたはミューテックスで、任意の数のスレッドを制御できます。

特定の例を対象としたものではなく、より一般的な教育上の発言として、スレッドは、KISS の概念が実際に適用される場所の 1 つです。派手になりすぎて、自分がすべて絡み合ってしまうのは非常に簡単です。

于 2009-10-15T02:49:29.883 に答える
0

これはあなたの質問に正確に答えるものではありませんが、スレッドからバリアを抽出することを検討することをお勧めします-それがC#の場合、それをオブジェクトにし、私はほとんどCを実行していませんが、おそらく構造体といくつかの関数。これは、この状況では大いに役立つ場合とそうでない場合がありますが、必要なときにバリアを最初から作成する手間を省くことができます。また、最初にセマフォを実装する場合は、セマフォに関してバリアを記述できます。これにより、作業が簡素化されます。(私は現在、.NETでの並行プログラミングに関する主題を行っています。私たちが行っていることの1つは、セマフォ、ミューテックス、バリア、ランデブー、チャネルなどの一連の同時実行ユーティリティを作成することです。これをプラグインできます。他のプログラムに。)

障壁については、Jamesがすでに述べたように、The Little BookofSemaphoresは正しい実装について説明しています

于 2009-10-15T02:01:35.587 に答える
0

セマフォを初期値1で初期化しているので、待機はすぐに成功します(操作の最後の投稿は、値を2に上げるだけです)。

sem_init(&t_1_sem, 0, 1);
sem_init(&t_2_sem, 0, 1);

次に、2番目の問題は、スレッドセーフではない方法でtid変数を生成していることです。この場合、ゼロの値が発生しなかったとしても、両方のスレッドがゼロの値で終了する可能性があります。

于 2009-10-18T13:22:52.980 に答える
0

これはあなたが望むものです:

i'm 0 and waiting for 1 - starting
i'm 1 and waiting for 0 - starting
i'm 1, 0 arrived, lets go
i'm 0, 1 arrived, lets go
i'm 1 and waiting for 0 - stopping
i'm 0 and waiting for 1 - stopping
i'm 0, 1 stopped, go home now
i'm 1, 0 stopped, go home now

そして正しいコード、元のコードの何が問題なのかを自分で見つけました。

#define SEM_INIT_V      0

static sem_t t_0_sem;
static sem_t t_1_sem;

void *thread(void *vargp);
/* shared by both threads*/
struct {
        int count;
} thread_count = {};

int main() {
        pthread_t tid0, tid1;

        thread_count.count = 0;

        sem_init(&t_0_sem, 0, SEM_INIT_V);
        sem_init(&t_1_sem, 0, SEM_INIT_V);
        pthread_create(&tid0, NULL, thread, &thread_count.count);
        thread_count.count++;
        pthread_create(&tid1, NULL, thread, &thread_count.count);

        pthread_join(tid0, NULL);
        pthread_join(tid1, NULL);

        return 0;
}

void *thread(void *vargp) {
        int tid = *(int*)vargp;

        //await to sync 0 & 1
        if (0 == tid) {
                puts("i'm 0 and waiting for 1 - starting");
                sem_post(&t_1_sem);
                sem_wait(&t_0_sem);
                puts("i'm 0, 1 arrived, lets go");
                sleep(8);
        } else {
                puts("i'm 1 and waiting for 0 - starting");
                sem_post(&t_0_sem);
                sem_wait(&t_1_sem);
                puts("i'm 1, 0 arrived, lets go");
                sleep(3);
        }

        if(tid == 0){
                puts("i'm 0 and waiting for 1 - stopping");
                sem_post(&t_1_sem);
                sem_wait(&t_0_sem);
                puts("i'm 0, 1 stopped, go home now");
        } else if(tid == 1){
                puts("i'm 1 and waiting for 0 - stopping");
                sem_post(&t_0_sem);
                sem_wait(&t_1_sem);
                puts("i'm 1, 0 stopped, go home now");
        }

        return NULL;
}
于 2009-10-15T03:01:36.970 に答える