首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C中的线程:生产者消费者需要永远运行

C中的线程:生产者消费者需要永远运行
EN

Stack Overflow用户
提问于 2018-02-01 05:50:25
回答 4查看 969关注 1票数 3

我对线程的概念很陌生。我在C中处理生产者消费者问题,但是当与producer并行时,使用者线程不会运行。

我的代码如下:

代码语言:javascript
复制
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>

int S;
int E;
int F;

void waitS(){
    //printf("hbasd");
    while(S<=0);
    S--;
}

void signalS(){
    S++;
}

void waitE(){
    while(E<=0);
    E--;
}

void signalE(){
    E++;
}
void waitF(){
    while(F<=0);
    F--;
}

void signalF(){
    F++;
}
int p,c;

void* producer(void *n){
    int *j = (int *)n;
    int i = *j;
    while(1){
        waitS();
        waitE();
        printf("Producer %d\n",E);
        signalS();
        signalF();
        p++;
        if(p>=i){
            printf("Exiting: producer\n");
            pthread_exit(0);
        }
    }
}

void* consumer(void *n){
    int *j = (int *)n;
    int i = *j;
    while(1){
        waitS();
        waitF();
        printf("Consumer %d\n",E);
        signalS();
        signalE();
        c++;
        if(c>=i){
            printf("Exiting Consumer\n");
            pthread_exit(0);
        }
    }
}


int main(int argc, char* argv[]){

    int n = atoi(argv[1]);

    E = n;
    S = 1;
    F = 0;

    int pro = atoi(argv[2]);
    int con = atoi(argv[3]);


    pthread_t pid, cid;
    pthread_attr_t attr;

    pthread_attr_init(&attr);

    pthread_create(&pid,&attr,producer,(void *)&pro);
    pthread_create(&cid,&attr,consumer,(void *)&con);

    pthread_join(pid,NULL);
    pthread_join(cid,NULL);

}

当输入为./a.out 3 4 3,即n=3,pro = 4,con =3时

我没办法出去,只是一种死锁的情况。

我希望输出像

生产者2生产者1生产者0消费者0消费者1生产者0退出:生产者消费者0退出:消费者

...similar输出,其中生产者运行4次,消费三次

当我给出像./a.out 4 4 3这样的输入时,我得到以下输出

生产者3生产者2生产者1生产者0退出:生产者消费者0消费者1消费者退出:消费者

从结果中,我得到了一个结论,即线程生产者正在执行第一,然后是线程使用者。

我希望它们同时执行,这样当给出像3,4,3这样的测试用例时,我得到一个类似于第一个预期输出的答案。

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2018-02-05 10:07:47

通常,您应该使用同步原语,但与其他答案不同,我认为,如果我们在x86体系结构上运行这个程序,可以防止编译器优化代码中的一些关键部分,那么我们可能不需要任何答案。

根据维基百科,x86体系结构几乎具有顺序一致性,这足以实现生产者-消费者算法。

成功实现这种生产者-消费者算法的规则非常简单:

  1. 我们必须避免从不同的线程中写入相同的变量,即如果一个线程写入变量X,另一个线程只从X读取。
  2. 我们必须明确地告诉编译器,我们的变量可能会在某个地方发生变化,即对线程变量之间的所有共享使用volatile关键字。

下面是基于您的代码的工作示例。生产者生产的数字从5降到0,消费者消耗它们。请记住,由于其他体系结构的排序较弱,这只适用于x86:

代码语言:javascript
复制
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>

volatile int P = 0;
volatile int C = 0;
volatile int value = 0;

void produce(int v)
{
    value = v;
    P++;
}
int consume()
{
    int v = value;
    C++;
    return v;
}

void waitForConsumer()
{
    while (C != P)
        ;
}

void waitForProducer()
{
    while (C == P)
        ;
}


void *producer(void *n)
{
    int i = *(int *)n;
    while (1) {
        waitForConsumer();
        printf("Producing %d\n", i);
        produce(i);
        i--;
        if (i < 0) {
            printf("Exiting: producer\n");
            pthread_exit(0);
        }
    }
}

void *consumer(void *n)
{
    while (1) {
        waitForProducer();
        int v = consume();
        printf("Consumed %d\n", v);
        if (v == 0) {
            printf("Exiting: consumer\n");
            pthread_exit(0);
        }
    }
}

int main(int argc, char *argv[])
{

    int pro = 5;

    pthread_t pid, cid;
    pthread_attr_t attr;

    pthread_attr_init(&attr);

    pthread_create(&pid, &attr, producer, (void *)&pro);
    pthread_create(&cid, &attr, consumer, NULL);

    pthread_join(pid, NULL);
    pthread_join(cid, NULL);
}

产生以下结果:

代码语言:javascript
复制
$ ./a.out
Producing 5
Producing 4
Consumed 5
Consumed 4
Producing 3
Producing 2
Consumed 3
Consumed 2
Producing 1
Producing 0
Exiting: producer
Consumed 1
Consumed 0
Exiting: consumer

要了解更多信息,我推荐Herb的演示文稿“atomic<> Weapons”,它很长,但是您需要知道关于排序和atomics的所有信息。

尽管上面列出的代码在x86上可以正常工作,但我确实鼓励您查看上面的演示文稿,并使用内置的atomics,比如__atomic_load_n(),它将在任何平台上生成正确的组装代码。

票数 1
EN

Stack Overflow用户

发布于 2018-02-01 06:22:48

您正在访问来自不同线程的非原子变量,而没有任何类型的同步;这是一种争用条件,它会导致未定义的行为。

特别是,现代CPU为每个CPU核心提供了独立的寄存器和单独的缓存,这意味着如果运行在CPU核心#1上的线程修改了变量的值,这种修改可能会在CPU #1的缓存中保留很长一段时间,而不会被“推送”到RAM,因此在CPU核心#2上运行的另一个线程可能在很长一段时间内不会“看到”线程#1的更新(或者永远不会)。

处理此问题的传统方法是使用一个或多个互斥变量(请参阅pthread_mutex_init()pthread_mutex_lock()pthread_mutex_unlock()等)序列化对共享变量的访问,或者使用原子变量而不是标准ints来访问要同时从多个线程访问的值。这两种机制都有保护措施,以确保不发生未定义的行为(如果使用正确的话)。

票数 2
EN

Stack Overflow用户

发布于 2018-02-01 08:27:15

在没有同步的情况下,不能从两个不同的线程访问相同的内存。线程的标准清楚地说明了它的这里

应用程序应确保由多个控制线程(线程或进程)对任何内存位置的访问受到限制,以便控制线程不能读取或修改内存位置,而另一个控制线程可能正在修改该位置。这种访问受到限制,使用同步线程执行的函数,并与其他线程同步内存。

此外,即使我们忽略了许多CPU不同步内存,除非您明确要求它们同步,但在普通C中,您的代码仍然是不正确的,因为如果可以在后台更改变量,它们应该是不稳定的。但是,即使在某些CPU上易失性可能会有所帮助,但对于p线程来说,这是不正确的。

只要使用适当的锁,不要旋转全局变量,有方法来加热比使用CPU便宜得多的房间。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/48556171

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档