C信号量和线程之间的“障碍”

时间:2009-10-15 01:17:14

标签: c multithreading semaphore

我正在努力解决我们的操作系统教授在之前的考试中向我们展示的问题,为下一个考试做准备。

问题是有两个并发执行的线程,可能会在不同的时间内完成。特定线程完成后,需要阻塞直到另一个线程完成,然后才能继续执行。

对我而言,这在概念上看起来很简单,但我的代码并没有按照我认为应该的方式运作。

#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,然后两个“最终值”printf发生在彼此旁边。然而,我所看到的是在线程完成计数到10之后立即发生的“最终值”打印 - 它似乎没有等待。

我在“发布到N”sem_val中打印的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

任何想法/提示?

7 个答案:

答案 0 :(得分:7)

您可以咨询The Little Book of Semaphores。第3.5节描述了障碍模式以及如何正确实现。

我知道这并没有直接回答你的问题,但它应该指向正确的方向。

答案 1 :(得分:6)

在osx上没有实现sem_getvalue()。 http://discussions.apple.com/thread.jspa?messageID=9404131&tstart=0

答案 2 :(得分: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;
}

答案 3 :(得分:1)

我刚刚使用带有互斥锁和条件变量的pthreads成功处理了类似的事情。

信号量是一种通用的线程间或进程间消息传递机制,您可以偶然使用它来进行线程协调,但有些困难。互斥体是专门的二进制信号量,专门针对线程协调,并提供简化的API来实现这一点。

因此,如果您不限制使用信号量,则可以将互斥锁视为一种更简单的方法来完成工作。

无论您选择哪种线程协调方法,都可以使用搜索引擎查找解决类似问题的代码示例,并确保您了解它们。例如,“pthread信号量示例”和“pthread互斥示例”都有很多有趣的点击。

一个很大的提示:确保你确实需要两个信号量。一个信号量或互斥量可以控制任意数量的线程。

作为更普遍的教学评论,并非针对您的具体示例,线程是KISS概念真正适用的地方之一。很容易变得过于花哨,让自己陷入困境。

答案 4 :(得分:0)

这并不完全回答你的问题,但是你可能想看看从线程中提取屏障 - 如果它是C#你会把它变成一个对象,而我几乎没有做过任何C,但是你' d可能有一个结构和几个函数。在这种情况下,这可能会或可能不会有很大帮助,但每次您需要时,它可以省去从头开始编写屏障。此外,如果您首先实现信号量,则可以根据信号量编写屏障,从而简化操作。 (我目前正在研究.NET中并发编程的主题,我们正在做的一件事就是编写一组并发实用程序 - 信号量,互斥量,屏障,集合点,通道等 - 我们可以插入它们到其他程序。)

至于障碍,正如詹姆斯已经提到的那样,The Little Book of Semaphores描述了正确的实现

答案 5 :(得分: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;
}

答案 6 :(得分:0)

您正在初始化信号量,初始值为1,因此等待将立即成功(操作结束时的帖子只会将值提升为2)。

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

然后第二个问题是你以非线程安全的方式生成 tid 变量。即使在这种情况下没有发生,两个线程也可能以零值结束。