【问题标题】:A segmentation fault (core dumped) in producer and consumer using semaphore使用信号量的生产者和消费者中的分段错误(核心转储)
【发布时间】:2018-09-25 14:17:39
【问题描述】:

我正在学习Linux系统编程,所以我只是尝试编写一个非常常见的程序,即生产者和消费者。我使用信号量机制来实现我的代码。不幸的是,我遇到了一个分段错误,我真的找不到我错的地方。希望有人能帮帮我,谢谢。

代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <pthread.h>
#include <semaphore.h>

sem_t sem_consumer;
sem_t sem_producer;

typedef struct node
{   
    int data;
    struct node *next;
} Node;

Node * head = NULL;

void * producer(void *arg)
{
    while (1)
    {
        sem_wait(&sem_producer);
        Node *ptr = (Node *)malloc(sizeof(Node));
        ptr->data = rand() % 1000;
        printf("++++++ producer: %lu, %d\n", pthread_self(), ptr->data);
        ptr->next = head;
        head = ptr;
        sem_post(&sem_consumer);
    }

    return NULL;
}

void * consumer(void * arg)
{
    while (1)
    {
        sem_wait(&sem_consumer);
        Node *pdel = head;
        head = head -> next;
        printf("------ consumer: %lu, %d\n", pthread_self(), pdel->data);
        free(pdel);
        sem_post(&sem_producer);
    }

    return NULL;
}

int main(int argc, char *argv[])
{
    sem_init(&sem_consumer, 0, 0);
    sem_init(&sem_producer, 0, 3);

    pthread_t pthid[2];

    pthread_create(&pthid[0], NULL, producer, NULL);
    pthread_create(&pthid[1], NULL, consumer, NULL);

    for (int i = 0; i < 2; i++)
    {
        pthread_join(pthid[i], NULL);
    }

    sem_destroy(&sem_consumer);
    sem_destroy(&sem_producer);

    return 0;
}

【问题讨论】:

  • 在gdb中运行可能是然后bt
  • 对于这些类型的错误,请使用消毒剂(在这种情况下,ThreadSanitizer
  • 另请注意,printf() 语句(例如 printf("------ consumer: %lu, %d\n", pthread_self(), pdel-&gt;data);)不能保证以原子方式发出,因此如果您的代码同时运行线程,您也可能会获得交错输出。您发布的代码不应同时运行生产者和消费者线程,因此不会变得明显。
  • 不是你的问题的原因,但你不应该在信号量锁内调用 printf,那将是非常低效的。而是抓取信号量,将相关部分复制到局部变量,释放信号量,打印局部变量。
  • @Lundin 你说得对,下次我会处理的。

标签: c linux consumer producer


【解决方案1】:

这个

sem_init(&sem_producer, 0, 3);

sem_producer信号量初始化为3,允许生产者和消费者线程同时访问诸如

之类的代码
ptr->next = head;
head = ptr;

Node *pdel = head;
head = head -> next;
printf("------ consumer: %lu, %d\n", pthread_self(), pdel->data);
free(pdel);

您的链表没有受到信号量的适当保护。这是一个竞争条件。

sem_producer 初始化为 1 将纠正竞争条件:

sem_init(&sem_producer, 0, 1);

可能还有其他我没有发现的错误。

【讨论】:

  • @hellow 如果您要更改答案的含义,请评论原因。我还没有充分分析 OP 的代码来说明用 1 而不是 3 的值初始化信号量会产生正确的结果。
  • ...你确定吗? sem_init(&amp;sem_producer, 0, 3); 将允许 producer() 循环三遍……问题不在于用两个信号量保护一个资源 (head) 吗?
  • @Attie 循环将与消费者线程同时发生,同时也修改链表。我没有对代码进行足够的分析,无法确定一旦进入该状态会发生什么。
  • @Andrew Henle 比赛条件,我明白了。那么我该如何避免这种情况,我的意思是我应该怎么做才能安全地最大化生产者的缓冲区,因为我不希望信号量只有一个。
  • 是的,但那时它将是一个列表(为什么不只有一个指针)。 大概不是 OP 想要从这样的概念验证中得到的......(我认为
【解决方案2】:

我认为主要问题是:

  • 你有一个资源 - head
  • 你正在用两个单独的信号量“保护”它 - sem_producersem_consumer

信号量用于表示“你可以拿东西”或“你可以放东西”,控制资源使用 - 例如,如果你想确保该列表永远不会比三个对象更深。当生产者/消费者线程具有不同的执行时间时,这一点尤其重要。如果您不太担心生产大量消费者可能永远无法跟上的商品,那么您可以完全删除sem_producer

互斥体(“互斥”)用于确保两个线程不会同时处理一个对象。我建议您在信号量旁边的指针操作周围使用互斥锁。

pthread_mutex_t mux;
void *producer(void *arg) {
    while (1) {
        sem_wait(&sem_producer);

        /* gather data */

        pthread_mutex_lock(&mux);
        p_new->next = head;
        head = p_new;
        pthread_mutex_unlock(&mux);

        sem_post(&sem_consumer);
    }

    return NULL;
}
void *consumer(void *arg) {
    while (1) {
        sem_wait(&sem_consumer);

        pthread_mutex_lock(&mux);
        p_next = head;
        if (p_next != NULL) {
            head = p_next->next;
        }
        pthread_mutex_unlock(&mux);

        /* skip if there isn't actually a new item */
        if (p_next != NULL) {
            /* do processing and discard */
        }

        sem_post(&sem_producer);
    }

    return NULL;
}

别忘了致电pthread_mutex_init()pthread_mutex_destroy()

【讨论】:

  • 正如目前所发布的,如果pdelNULL 并且生产者线程正在等待sem_producer 信号量,则线程将死锁。有了互斥体,完全消除sem_producer 信号量在逻辑上是安全的。实际上,如果生产者线程超前,最大可能的信号量值可能会发挥作用。
  • @Attie 我认为消费者中 pthread_mutex_unlock 方法之后的 if 语句根本不需要,因为我有 sem_consumer 信号量来确保我在 sem_wait 非阻塞时肯定有项目。而且您错过了在消费者中释放 pdel。
  • 我认为消费者中的 pthread_mutex_unlock 方法之后的 if 语句根本不需要” - 可能,但如果你接受“what if”场景,然后进行测试,它将防止您的应用程序崩溃...sem_wait() 可以返回除了“you got a poke”之外的原因。
  • "你错过了释放消费者中的 pdel" - 我从示例代码中删除了内存管理...应该还有更多的返回值检查!.. . 为简洁起见省略 :-)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多