多线程程序使用者导致程序冻结

时间:2017-10-11 13:06:10

标签: c multithreading mutex

我写了多线程程序,生产者工作正常。我知道这是因为我禁用了消费者而生产者给出了预期的输出。生成器用于以随机值1 - 10填充共享缓冲区,生成器轮流填充缓冲区。缓冲区中的所有元素最初都设置为-1,向生产者指示它们是空的。然后,生产者应该通知消费者它已经填充了缓冲区的一个元素并且消耗它。当我添加消费者时,程序会填充一些元素,消耗1然后冻结。

这是我收到消费者时收到的输出。程序信号一次,产生几行,然后冻结。正如我所说,当消费者没有添加到生产者的工作正如预期。我一直在研究这个问题,我无法弄清楚这一点。我认为这个问题与消费者有关。

Producer thread 976 and value: 3
Producer thread 231312 and value: 3
Signal
Producer thread 976 and value: 3
Producer thread 231312 and value: 3
buff[1] = 3 and thread = 1232
Producer thread 976 and value: 2
Producer thread 231312 and value: 2
Producer thread 297568 and value: 3
Producer thread 298080 and value: 3
Producer thread 298592 and value: 3

对于这种类型的程序,消费者应该如何看待?

这是程序

#include <stdio.h>
    #include <string.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <pthread.h>
    #include <stdint.h>

    #define THREADS 5
    #define ELEMENTS 120



    int value = 0;
    int saveValue = 0;
    void *produce(void *arg);
    void *consume(void *arg);
    int producerCount =0;
    int consumerCount = ELEMENTS;

    struct {
      pthread_mutex_t mutex;
      int index;
      int value; 
      int MyShBuff[ELEMENTS];
    } add = {PTHREAD_MUTEX_INITIALIZER, 0, 0}; 


      struct{
       pthread_mutex_t    mutex;
       pthread_cond_t     cond;
       int nready;
       int value;
       int empty;
       int counter;

       /* number ready for consumer */
    } nready = {PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER,0, -2, ELEMENTS};

    int main()
    {
        pthread_t tid_producer[THREADS], tid_consumer[THREADS];
        int i, j, k;

        //Ready buffer for producers
        for (i =0; i < ELEMENTS; i++)
        {
            add.MyShBuff[i]=-1;
        }



        for(j = 0; j < THREADS; j++) {

       pthread_create(&tid_producer[j], NULL, &produce, NULL);
       pthread_create(&tid_consumer[j], NULL, &consume, NULL);
    }



     /* wait for all producers and the consumer*/

        for(k = 0; k < THREADS; k++) {
            pthread_join(tid_producer[k], NULL);
            pthread_join(tid_consumer[k], NULL);    
        }


        exit(0);    
        return 0;
    }

    void *produce(void *arg)
    { 
    int i = 0;


    for ( ; ; ) 
    {
        pthread_mutex_lock(&add.mutex);
        if(add.index  >= ELEMENTS)
        {
            pthread_mutex_unlock(&add.mutex);
            return NULL;
        }
        if(add.MyShBuff[add.index] == -1)
        {
         add.value = rand() % 10 + 0;   
         add.MyShBuff[add.index] = add.value;
         printf("Producer thread %d and value: %d\n" ,pthread_self(), add.MyShBuff[add.index]);
         add.index++;
        }
         pthread_mutex_unlock(&add.mutex);
         pthread_mutex_lock(&nready.mutex);
         if(nready.nready == 0)
         {
         pthread_cond_signal(&nready.cond);
         printf("Signal\n");
         }

        nready.nready++;
        pthread_mutex_unlock(&nready.mutex);

        }


    }


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

      while(nready.empty != 0)
      {
       pthread_mutex_lock(&nready.mutex);
        while (nready.nready == 0)
        {
           pthread_cond_wait(&nready.cond,&nready.mutex);
            nready.nready--;
             nready.empty--;
             nready.counter++;
             pthread_mutex_unlock(&nready.mutex);

            pthread_mutex_lock(&add.mutex);
             printf("buff[%d] = %d and thread = %d\n", nready.counter, add.MyShBuff[nready.counter], pthread_self());
             add.MyShBuff[nready.counter] = -2;
             pthread_mutex_unlock(&add.mutex);



        }

      }

            return NULL;

    }

我认为我提出了建议的更改。这是包含编辑的程序。

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
#include <stdint.h>

#define THREADS 5
#define ELEMENTS 120



int value = 0;
int saveValue = 0;
void *produce(void *arg);
void *consume(void *arg);
int producerCount =0;
int consumerCount = ELEMENTS;

struct {
  pthread_mutex_t mutex;
  int index;
  int value; 
  int MyShBuff[ELEMENTS];
} add = {PTHREAD_MUTEX_INITIALIZER, 0, 0}; 


  struct{
   pthread_mutex_t    mutex;
   pthread_cond_t     cond;
   int nready;
   int value;
   int empty;
   int counter;

   /* number ready for consumer */
} nready = {PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER,0, -2, ELEMENTS};

int main()
{
    pthread_t tid_producer[THREADS], tid_consumer[THREADS];
    int i, j, k;

    //Ready buffer for producers
    for (i =0; i < ELEMENTS; i++)
    {
        add.MyShBuff[i]=-1;
    }



    for(j = 0; j < THREADS; j++) {

   pthread_create(&tid_producer[j], NULL, &produce, NULL);
   pthread_create(&tid_consumer[j], NULL, &consume, NULL);
}



 /* wait for all producers and the consumer*/

    for(k = 0; k < THREADS; k++) {
        pthread_join(tid_producer[k], NULL);
        pthread_join(tid_consumer[k], NULL);    
    }

     /* Clean up and exit */

  pthread_mutex_destroy(&nready.mutex);
  pthread_mutex_destroy(&add.mutex);
   pthread_cond_destroy(&nready.cond);
   pthread_exit(NULL);

    exit(0);    
    return 0;
}

 void *produce(void *arg)
    { 
    int i = 0;


    for ( ; ; ) 
    {
        pthread_mutex_lock(&add.mutex);
        if(add.index  >= ELEMENTS)
        {
            pthread_mutex_unlock(&add.mutex);
            return NULL;
        }
        if(add.MyShBuff[add.index] == -1)
        {
         add.value = rand() % 10 + 0;   
         add.MyShBuff[add.index] = add.value;
         printf("Producer thread %d and value: %d\n" ,pthread_self(), add.MyShBuff[add.index]);
         add.index++;
        }
         pthread_mutex_unlock(&add.mutex);
         pthread_mutex_lock(&nready.mutex);
         if(nready.nready == 0)
         {
         pthread_cond_broadcast(&nready.cond);
         printf("Signal\n");
         }

        nready.nready++;
        pthread_mutex_unlock(&nready.mutex);

        }


    }


void *consume(void *arg)
{
pthread_mutex_lock(&nready.mutex);

  while(nready.empty != 0)
  {

    while (nready.nready == 0)
    {
        pthread_cond_wait(&nready.cond,&nready.mutex);

         nready.nready--;
         nready.empty--;

         pthread_mutex_unlock(&nready.mutex);

        pthread_mutex_lock(&add.mutex);
         printf("buff[%d] = %d and thread = %d\n", nready.counter, add.MyShBuff[nready.counter], pthread_self());
         add.MyShBuff[nready.counter] = -2;
         pthread_mutex_unlock(&add.mutex);

        pthread_mutex_lock(&nready.mutex);
        nready.counter++;
        pthread_mutex_unlock(&nready.mutex);

    }
  }


        return NULL;

}

第二次编辑消费者()。

void *consume(void *arg)
{

pthread_mutex_lock(&nready.mutex);

  while(nready.empty != 0)
  {

    while (nready.nready == 0)
    {


        pthread_cond_wait(&nready.cond,&nready.mutex);


         pthread_mutex_lock(&add.mutex);
         printf("buff[%d] = %d and thread = %d\n", nready.counter, add.MyShBuff[nready.counter], pthread_self());
         add.MyShBuff[nready.counter] = -2;
         pthread_mutex_unlock(&add.mutex);

        nready.counter++;   
        nready.empty--;
        printf("Empty %d\n" , nready.empty);

    }

    nready.nready--;

  }


  return NULL;


}

1 个答案:

答案 0 :(得分:2)

您的consume()函数在使用共享变量和同步对象时至少存在四个问题:

  1. 在外部while条件下,它在任何互斥锁(或信号量)的保护之外读取共享变量nready.empty。在其他地方,修改该变量的值。因此,您的程序行为未定义。

  2. 它将内部循环中的互斥nready.mutex 锁定在之外,但在内解锁它(并且不会重新锁定它)。因此,如果内循环在外循环的给定迭代上迭代多次 - 这非常合理地可能 - 那么

    • 在第二次和后续迭代中,该函数尝试访问并可能修改共享nready结构的其他成员而不保护互斥锁。

    • 在第二次和后续迭代中,函数尝试在条件变量nready.cond上等待而不锁定为此目的指定的互斥锁(nready.mutex

    • 在第二次及以后的迭代中,该函数尝试解锁一个未锁定的互斥锁。

  3. 似乎假设在所有线程上聚合,它将通过条件变量为每个线程接收一个信号。这不是一个安全的假设,因为

    • 信号未排队。如果生产者线程在没有消费者等待时发送信号,则该信号将丢失。此外,

    • 可能发生虚假唤醒,其中一个线程从pthread_cond_wait()返回而没有对pthread_cond_signal()进行相应的调用。这些都是不寻常的,但确实发生了,你需要为它们做好准备。

  4. 当没有更多工作要做时,您没有关闭消费者线程的规定。实际处理最后一个工作单元的那个可能会退出,但所有其他单元可能会被卡住,无限期地等待条件变量。

  5. 条件变量的命名是因为它们是围绕允许线程暂停操作直到某些条件变为真的概念而设计的。等待条件变量的传统使用范例如下:

    1. 锁定关联的互斥锁。
    2. 可选择执行一些工作。
    3. 检查CV的相关情况。对条件所基于的任何共享变量的访问必须受到与CV一起使用的相同互斥锁的保护。
    4. 如果满足条件,则无需等待即可继续(跳至步骤6);否则开始等待CV
    5. 从等待返回后,返回第3步。必须重新检查条件,以确保它是真的,以免它被其他线程再次弄错,或者从一开始就不是真的。
    6. 可选择执行一些工作。
    7. 解锁互斥锁。
    8. 如上所述,您可能会发现依靠pthread_cond_broadcast()代替pthread_cond_signal()可以获益。特别是,这可能构成解决消费者关闭问题的一部分 - 但不是全部 - 。