简体   繁体   English

在C中使用Pthreads互斥锁和条件变量进行同步

[英]Synchronization using Pthreads mutex and conditional variables in C

I am trying to create two threads resembling TaskA and TaskB. 我试图创建两个类似于TaskA和TaskB的线程。 Both TaskA and TaskB do some kind of computation that it is not very interesting for this post. TaskA和TaskB都进行某种形式的计算,因此对于本篇文章来说不是很有趣。 TaskA and TaskB have to be executed 10 times in order to cover the whole array. TaskA和TaskB必须执行10次才能覆盖整个数组。 TaskA has an input AA and an output BB. TaskA具有输入AA和输出BB。 BB is also the input of TaskB. BB也是TaskB的输入。 CC is the output of TaskB. CC是TaskB的输出。 Because BB is written by taskA and read by taskB we need mutexes. 因为BB是由taskA编写的,而由taskB读取的,所以我们需要互斥锁。

The behavior I would like to achieve is that when TaskA operates on i, TaskB operates on i-1 in parallel, where i is the number of arrays that are processed. 我想实现的行为是,当TaskA在i上进行操作时,TaskB在i-1上并行进行操作,其中i是要处理的数组数。 I want to avoid TaskB to wait for TaskA to finish for every i. 我想避免TaskB为每个i等待TaskA完成。

The problem here is that I have a deadlock. 这里的问题是我陷入了僵局。 ThreadA and ThreadB represent TaskA and TaskB. ThreadA和ThreadB代表TaskA和TaskB。 To make it easier I removed all the computations and I left only synchronization instructions. 为了简化起见,我删除了所有计算,只留下了同步指令。 The deadlock is caused because ThreadA signals the conditional variable CV[0] before threadB is in the state that waits for CV[0]. 导致死锁的原因是,在线程B处于等待CV [0]的状态之前,ThreadA发出了条件变量CV [0]的信号。

Do you know any way to remove the deadlock but without TaskA waiting for TaskB to finish and vice versa. 您是否知道消除死锁的任何方法,但没有TaskA等待TaskB完成,反之亦然。 Ideally when TaskA operates on array i TaskB should operate on array i-1. 理想情况下,当TaskA在阵列i上运行时,TaskB应该在阵列i-1上运行。

/* Includes */
#include <unistd.h>     /* Symbolic Constants */
#include <sys/types.h>  /* Primitive System Data Types */ 
#include <errno.h>      /* Errors */
#include <stdio.h>      /* Input/Output */
#include <stdlib.h>     /* General Utilities */
#include <pthread.h>    /* POSIX Threads */
#include <string.h>     /* String handling */
#include <semaphore.h>  /* Semaphore */
#include <stdint.h>

#define ARRAY_SIZE 2048*2400
#define DEBUG
//#define CHECK_RESULTS

pthread_mutex_t mutex[10];
pthread_cond_t cv[10];

/* prototype for thread routine */
void threadA ( void *ptr );
void threadB ( void *ptr );


struct thread_arg
{
    uint32_t *in;
    uint32_t *out;
    uint32_t ID;    
};

int main()
{

    pthread_t pthA;
    pthread_t pthB;
   //Memory allocation 
    uint32_t *AA = malloc(10*ARRAY_SIZE*sizeof(uint32_t)); 
    uint32_t *BB = malloc(10*ARRAY_SIZE*sizeof(uint32_t));
    uint32_t *CC = malloc(10*ARRAY_SIZE*sizeof(uint32_t)); 
    unsigned int j,i;

    // THread Arguments
    struct thread_arg arguments[2];
    arguments[0].in  = AA;
    arguments[0].out = BB;
    arguments[0].ID  = 1;
    arguments[1].in  = BB;
    arguments[1].out = CC;
    arguments[1].ID  = 2;
    //Init arguments data
    for (j=0;j<10;j++)
    {
        for (i=0;i<ARRAY_SIZE;i++)
        {
            AA[j*ARRAY_SIZE+i] = i;
            BB[j*ARRAY_SIZE+i] = 0;
            CC[j*ARRAY_SIZE+i] = 99 ; 
        }
    }   

    //Semaphore and conditional variables init
    for (i=0;i<10;i++){
        pthread_mutex_init(&mutex[i], NULL);
        pthread_cond_init (&cv[i], NULL);

    }

    pthread_create (&pthA, NULL, (void *) &threadA, (void *) &arguments[0]);
    pthread_create (&pthB, NULL, (void *) &threadB, (void *) &arguments[1]);

    pthread_join(pthA, NULL);
    pthread_join(pthB, NULL);

    // Destroy Semaphores and CVs
    for (i=0;i<10;i++)
    {
        pthread_mutex_destroy(&mutex[i]);
        pthread_cond_destroy(&cv[i]); 
    }       
    // Checking results 

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





void threadA ( void *ptr )
{
    int i; 
    struct thread_arg *arg = (struct thread_arg *) ptr;
    for (i=0;i<10;i++)
    {
        pthread_mutex_lock(&mutex[i]);
        printf("TA: LOCK_M%d  \n",i);
        pthread_cond_signal(&cv[i]);
        printf("TA: SIG_CV%d\n",i);
        pthread_mutex_unlock(&mutex[i]);
        printf("TA: UNL_M%d\n",i);
    }   
    pthread_exit(0); /* exit thread */
}




void threadB ( void *ptr )
{
    int i; 
    struct thread_arg *arg = (struct thread_arg *) ptr;

    for (i=0;i<10;i++)
    {
        pthread_mutex_lock(&mutex[i]);
        printf("TB: WAIT_CV%d\n",i,i);
        pthread_cond_wait(&cv[i], &mutex[i]);
        printf("TB CV%d_PASSED\n",i);
        pthread_mutex_unlock(&mutex[i]);
        printf("TB UNL_M%d \n",i);
    }

  pthread_exit(NULL);
}

As WhozCraig commented, a condition variable needs to be paired with a condition over some shared state, known as a predicate . 正如WhozCraig所说,条件变量需要与某个共享状态(称为谓词)上的条件配对。 The mutex is used to protect the shared state. 互斥锁用于保护共享状态。

In this example, your shared state could be an integer that contains the highest index of BB[] that ThreadA has produced. 在此示例中,您的共享状态可以是一个整数,其中包含ThreadA产生的BB[]的最高索引。 ThreadB then waits for this number to reach the index that it is up to reading. 然后,ThreadB等待该数字达到它要读取的索引。 In this design, you only need one mutex and one condition variable. 在这种设计中,您只需要一个互斥锁和一个条件变量。 The globals would then be: 全局变量将是:

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cv = PTHREAD_COND_INITIALIZER;
int BB_ready = -1;  /* Protected by 'mutex' */

(Using the static PTHREAD_*_INITIALIZER initialisers means that you don't need to bother with pthread_*_init() and pthread_*_destroy() ). (使用静态PTHREAD_*_INITIALIZER初始化程序意味着您无需费心pthread_*_init()pthread_*_destroy() )。

The loop in ThreadA would then be: 这样,ThreadA中的循环将是:

for (i=0;i<10;i++)
{
    /* Process AA[i] into BB[i] here */

    /* Now mark BB[i] as ready */
    pthread_mutex_lock(&mutex);
    printf("TA: LOCK_M%d  \n",i);
    BB_ready = i;
    pthread_cond_signal(&cv);
    printf("TA: SIG_CV%d\n",i);
    pthread_mutex_unlock(&mutex);
    printf("TA: UNL_M%d\n",i);
}

..and in ThreadB: ..和ThreadB中:

for (i=0;i<10;i++)
{
    /* Wait for BB[i] to be ready */
    pthread_mutex_lock(&mutex);
    printf("TB: WAIT_CV%d\n",i);
    while (BB_ready < i)
        pthread_cond_wait(&cv, &mutex);
    printf("TB CV%d_PASSED\n",i);
    pthread_mutex_unlock(&mutex);
    printf("TB UNL_M%d \n",i);

    /* Now process BB[i] into CC[i] here */
}

Notice that pthread_cond_signal() is called whenever the shared state has changed, which allows the other thread to wake up and re-check the state, if it's waiting. 请注意,只要共享状态发生更改,就会调用pthread_cond_signal() ,这将使另一个线程唤醒并重新检查该状态(如果正在等待)。

The waiting thread always loops around, checking the state and then waiting on the condition variable if the state isn't ready yet. 等待线程始终循环运行,检查状态,如果状态尚未准备就绪,则等待条件变量。

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

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