简体   繁体   English

多线程,消费者和生产者

[英]Multi-threading, consumers and producers

I have a problem with multithreading, since I'm new to this topic. 由于我是本主题的新手,因此我在多线程方面遇到问题。 Code below is code I've been given from my University. 下面的代码是我从大学获得的代码。 It was in few versions, and I understood most of them. 它只有几个版本,而我大部分都了解。 But I don't really understand the nready.nready variable and all this thread condition. 但是我不太了解nready.nready变量以及所有这些线程条件。 Can anyone describe how those two work here? 谁能描述这两个在这里的工作方式? And why can't I just synchronise work of threads via mutex? 为什么我不能仅通过互斥量同步线程的工作?

#include    "unpipc.h"

#define MAXNITEMS       1000000
#define MAXNTHREADS         100

    /* globals shared by threads */
int     nitems;             /* read-only by producer and consumer */
int     buff[MAXNITEMS];

struct {
pthread_mutex_t mutex;
pthread_cond_t  cond;
int             nput;
int             nval;
int             nready;
} nready = { PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER };

void    *produce(void *), *consume(void *);

/* include main */
int
main(int argc, char **argv)
{
int         i, nthreads, count[MAXNTHREADS];
pthread_t   tid_produce[MAXNTHREADS], tid_consume;

if (argc != 3)
    err_quit("usage: prodcons5 <#items> <#threads>");
nitems = min(atoi(argv[1]), MAXNITEMS);
nthreads = min(atoi(argv[2]), MAXNTHREADS);

Set_concurrency(nthreads + 1);
    /* 4create all producers and one consumer */
for (i = 0; i < nthreads; i++) {
    count[i] = 0;
    Pthread_create(&tid_produce[i], NULL, produce, &count[i]);
}
Pthread_create(&tid_consume, NULL, consume, NULL);

    /* wait for all producers and the consumer */
for (i = 0; i < nthreads; i++) {
    Pthread_join(tid_produce[i], NULL);
    printf("count[%d] = %d\n", i, count[i]);    
}
Pthread_join(tid_consume, NULL);

exit(0);
}
/* end main */

void *
produce(void *arg)
{
    for ( ; ; ) {
    Pthread_mutex_lock(&nready.mutex);
    if (nready.nput >= nitems) {
        Pthread_mutex_unlock(&nready.mutex);
        return(NULL);       /* array is full, we're done */
    }
    buff[nready.nput] = nready.nval;
    nready.nput++;
    nready.nval++;
    nready.nready++;
    Pthread_cond_signal(&nready.cond);
    Pthread_mutex_unlock(&nready.mutex);
    *((int *) arg) += 1;
}
}

/* include consume */
void *
consume(void *arg)
{
int     i;

for (i = 0; i < nitems; i++) {
    Pthread_mutex_lock(&nready.mutex);
    while (nready.nready == 0)
        Pthread_cond_wait(&nready.cond, &nready.mutex);
    nready.nready--;
    Pthread_mutex_unlock(&nready.mutex);

    if (buff[i] != i)
        printf("buff[%d] = %d\n", i, buff[i]);
}
return(NULL);
}
/* end consume */
pthread_mutex_lock(&nready.mutex);
while (nready.nready == 0)
    pthread_cond_wait(&nready.cond, &nready.mutex);
nready.nready--;
pthread_mutex_unlock(&nready.mutex);

The whole point of this structure is to guarantee that the condition (nready.nready == 0) is still true when you execute the corresponding action (nready.nready--) or - if the condition is not satisfied - to wait until it is without using CPU time. 该结构的全部目的是确保在执行相应的动作(nready.nready--)时条件(nready.nready == 0)仍然为true,或者-如果不满足条件-等待直到它满足无需占用CPU时间。

You could use a mutex only, to check that the condition is correct and to perform the corresponding action atomically. 您只能使用互斥锁,以检查条件是否正确并自动执行相应的操作。 But if the condition is not satisfied, you wouldn't know what to do. 但是,如果条件不满足,您将不知道该怎么办。 Wait? 等待? Until when? 到什么时候? Check it again? 再检查一次? Release the mutex and re-check immediately after? 释放互斥并在之后立即重新检查? That would be wasting CPU time... 那会浪费CPU时间...

pthread_cond_signal() and pthread_cond_wait() are here to solve this problem. pthread_cond_signal()和pthread_cond_wait()可以解决此问题。 You should check their man pages. 您应该检查他们的手册页。

Briefly, what pthread_cond_wait does, is it puts the calling thread to sleep and release the mutex in an atomic way until it's signaled. 简而言之,pthread_cond_wait的作用是使调用线程进入休眠状态,并以原子方式释放互斥锁,直到发出信号为止。 So this is a blocking function. 因此,这是一个阻止功能。 The thread can then be re-scheduled by calling signal or broadcast from a different thread. 然后可以通过调用信号或从其他线程广播来重新安排线程。 When the thread is signaled, it grabs the mutex again and exit the wait() function. 当线程发出信号时,它将再次获取互斥量并退出wait()函数。

Ath this point you know that 到目前为止,您知道

  1. your condition is true and 你的情况是真实的,
  2. you hold the mutex. 您持有互斥量。

So you can do whatever you need to do with your data. 因此,您可以做任何需要处理的数据。

Be careful though, you shouldn't call wait, if you're not sure that another thread will signal. 但是要小心,如果您不确定另一个线程是否会发出信号,则不应调用wait。 This is a very common source of deadlocks. 这是死锁的一个非常常见的来源。

When a thread received a signal, it's put on the list of threads that are ready to be scheduled. 当线程接收到信号时,它将被放入准备进行调度的线程列表中。 By the time the thread is actually executed, your condition (ie nread.nready == 0) may be false again. 在实际执行线程时,您的条件(即nread.nready == 0)可能再次为假。 Hence the while (to recheck if the thread is waked). 因此是while(以重新检查线程是否被唤醒)。

"But I don't really understand the nready.nready variable"

this results from the struct instance being named 'nready' and there 
is a field within the struct named 'nready'

IMO: a very poor design to have two different objects being given the same name

the nready field of the nready struct seems to be keeping track of the number of 
items that have been 'produced'

1) The nready filed of struct nready is used to tack how many tasks are ready to consume, ie, the remaining tasks in array buff . 1) nready日提交的struct nready用于粘性多少任务准备消耗,即在阵列的其余任务buff The nready.nready++; nready.nready++; statement is only executed when producers put one new item in array buff , and the nready.nready--; 仅当生产者将一个新项目放入数组buffnready.nready--;时才执行该nready.nready--; is only executed when consume gets item out of buff . 仅当消耗使物品失去buff时执行。 With is variable, programmer can always track how many tasks are there left to process. 使用is变量,程序员可以始终跟踪要处理的任务数量。

2) 2)

pthread_mutex_lock(&nready.mutex);
while (nready.nready == 0)
    pthread_cond_wait(&nready.cond, &nready.mutex);
nready.nready--;
pthread_mutex_unlock(&nready.mutex);

The statements above are common condition variable usage. 上面的语句是常见条件变量的用法。 You can check POSIX Threads Programming and Condition Variables for more about condition variables. 您可以检查POSIX线程编程条件变量,以获取有关条件变量的更多信息。

Why can't use mutex only? 为什么不能只使用互斥锁? You can poll a mutex lock again and again. 您可以一次又一次轮询互斥锁。 Obviously, it is CPU time consuming and may be hugely affect system performance. 显然,这很浪费CPU时间,并且可能会严重影响系统性能。 Instead, you want the consume to wait in sleep when there is no more items in buff , and to be awaken when producer puts new item in buff . 相反,您希望消耗者在buff没有更多物品时在睡眠中等待,并在生产者将新物品放入buff时唤醒。 The condition variable is acting as this role here. 条件变量在此充当此角色。 When there is no items (nready.nready==0), pthread_cond_wait() function puts the current thread into sleep, save the precious cpu time. 当没有任何项时(nready.nready == 0), pthread_cond_wait()函数将当前线程置于睡眠状态,从而节省了宝贵的CPU时间。 When new items are arriving, Pthread_cond_signal() awakes the consumer. 当有新商品到达时, Pthread_cond_signal()唤醒消费者。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM