使用互斥锁实现生产者/消费者

时间:2010-07-09 13:21:32

标签: c multithreading pthreads posix mutex

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
#define WORK_SIZE 1024
pthread_mutex_t work_mutex;
char work_area[WORK_SIZE];
void *thread_start(void *);
int main() {
pthread_t a_thread;
pthread_mutex_init(&work_mutex,NULL);
pthread_create(&a_thread,NULL,thread_start,NULL);
while(1)
{
pthread_mutex_lock(&work_mutex);
printf("Enter some text\n");
fgets(work_area, WORK_SIZE, stdin);
pthread_mutex_unlock(&work_mutex);
}
return 0;
}

void *thread_start(void *arg)
{
sleep(1);
while(1)
{
pthread_mutex_lock(&work_mutex);
printf("You enetered %d char",strlen(work_area));
pthread_mutex_unlock(&work_mutex);
}
}

当我执行程序时,在主线程中释放互斥锁之后,它再次在第二个线程可以获取锁之前获取锁。我期待一旦主线程释放锁,第二个已被阻塞的线程将获取锁并在主线程之前开始执行。

更清楚的是,我得到了这样的输出: -

Enter some text
qwerty
Enter some text
asdaf
Enter some text
jkdf 
Enter some text

6 个答案:

答案 0 :(得分:2)

这对你来说就是这样。在主数据输入之前锁定,这将比输出行所需的数量级大。在那段时间里,另一个线程除了阻止之外什么都不做。你的主要是释放锁定,微秒后再获取它。

如果这样做的时间足够长 - 可能数千次 - 你会发现它有效。但是最好只将main中的输入行复制到队列中,或者通过锁将一些其他内存保护。然后另一个线程将有机会获得它。

编辑:

一般的想法是这样的。我的代码添加很糟糕,但应该足够好用于说明。

int main()
{
    pthread_t a_thread;
    pthread_mutex_init(&work_mutex, NULL);
    pthread_create(&a_thread, NULL, thread_start, NULL);

    memset(work_area, '\0', sizeof(work_area));


    char input[WORK_SIZE - 1];

    while (1)
    {
        printf("Enter some text\n");
        fgets(input, WORK_SIZE, stdin);

        pthread_mutex_lock(&work_mutex);
        strcpy(work_area, input);
        pthread_mutex_unlock(&work_mutex);
    }

    return 0;
}

void *thread_start(void *arg)
{
    sleep(1);

    while (1)
    {
        pthread_mutex_lock(&work_mutex);

        if (work_area[0] != '\0')
        {
            printf("You enetered %d char\n", strlen(work_area));
            work_area[0] = '\0';
        }

        pthread_mutex_unlock(&work_mutex);
    }
}

答案 1 :(得分:1)

您可能想要创建并初始化信号量,然后在第二个线程中等待主函数在输入信号时发出事件信号。

检查条件等待和信号量。

第二个线程不知道主线程中生成了什么事件。 For your reference

答案 2 :(得分:1)

提出使用信号量的建议,我认为你观察到的行为的原因如下。

  1. 在调用thread_start中的pthread_mutex_lock之前,不会阻止main中的循环。
  2. 因此,thread_start的循环才有机会调用pthread_mutex_lock的唯一时间是执行main的线程的时间片到期时
  3. 锁定被释放时发生时间片到期的可能性微乎其微。这是因为主线程在等待ENTER键时从阻塞状态唤醒时可能会有一个新的时间片。
  4. 请注意,此解释假定为单个核心。但即使在多核系统上,thread_start线程也只会在另一个线程的时间片耗尽时进行调度。主要线程没有锁定的可能性很小。

    上述假设的一个可能的测试是在释放锁之后调用pthread_yield。您可能希望在两个线程中都这样做。即便如此,我也不认为每次都会保证线程切换。

答案 3 :(得分:0)

在阅读了torak给出的评论之后,我改变了代码。现在它按预期工作正常。

[root@localhost threads]# diff -Nurp mutex.c mutex_modified.c 
--- mutex.c 2010-07-09 19:50:51.000000000 +0530
+++ mutex_modified.c    2010-07-09 19:50:35.000000000 +0530
@@ -18,6 +18,7 @@ pthread_mutex_lock(&work_mutex);
 printf("Enter some text\n");
 fgets(work_area, WORK_SIZE, stdin);
 pthread_mutex_unlock(&work_mutex);
+sleep(1);
 }
 return 0;
 }
@@ -30,5 +31,6 @@ while(1)
 pthread_mutex_lock(&work_mutex);
 printf("You enetered %d char",strlen(work_area));
 pthread_mutex_unlock(&work_mutex);
+sleep(1);
 }
 }

但它仍然很混乱。虽然这是一个测试程序,但在编写实际应用程序时应该怎么做才能避免这种情况?

答案 4 :(得分:0)

这是一个稍微尴尬的解决方案,保证可以正常工作:#include

#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>

#define WORK_SIZE 1024
pthread_mutex_t work_mutex;
char input_area[WORK_SIZE];
char work_area[WORK_SIZE];

void *thread_start(void *);

int main()
{
    pthread_t a_thread;
    pthread_mutex_init(&work_mutex,NULL);
    work_area[0] = 0;

    pthread_create(&a_thread,NULL,thread_start,NULL);
    while(1) {
        printf("Enter some text\n");
        fgets(input_area, WORK_SIZE, stdin);
        pthread_mutex_lock(&work_mutex);
        while (work_area[0] != 0) {
            pthread_mutex_unlock(&work_mutex);
            sleep(1);
            pthread_mutex_lock(&work_mutex);
        }
        memcpy(work_area, input_area, WORK_SIZE);
        pthread_mutex_unlock(&work_mutex);
    }
    return 0;
}

void *thread_start(void *arg)
{
    while(1)
    {
        pthread_mutex_lock(&work_mutex);
        if (work_area[0] > 0) {
            printf("you enetered %d char\n",strlen(work_area));
            work_area[0] = 0;
            pthread_mutex_unlock(&work_mutex);
        } else {
            pthread_mutex_unlock(&work_mutex);
            sleep(1);
        }
    }
}

请注意,因为POSIX互斥锁是特定于线程的,所以消费者线程不能等待来自生产者线程的信号,因此它只是每秒唤醒以检查新数据。类似地,如果生产者需要将某些东西放入队列并且工作区域已满,它会等待几秒钟,直到消费者开始清空缓冲区。你可以使用pthread_yield而不是sleep来消除一些延迟,但是你的线程将“忙等待”,消耗大量的CPU来反复检查它们的条件是否满足

请注意,如果您希望它为0个字符的条目打印一行,您可以添加一个单独的bool来指示队列中是否有新数据。

答案 5 :(得分:0)

@torax *“注意,这个解释假设一个核心。但即使在多核系统上,thread_start线程也只会在另一个线程的时间片耗尽时进行调度”*

我认为这不是真的,主线程和thread_start可以在两个内核上并行调度

相关问题