线程同步---条件变量以及结合互斥锁构建生产者消费者模型

条件变量
什么是条件变量?
  • 条件变量是进行线程阻塞的一种机制,经常和互斥锁结合起来处理生产者消费者模型
  • 条件变量给多线程提供了一个会合的场所。条件变量与互斥锁一起使用时,允许线程以无竞争的方式等待特定的条件发生
  • 条件变量只有满足特定条件(如,任务队列已满或已空)时才会阻塞线程;如果条件不满足,多个线程可以同时进入临界区,同时读写共享资源,因此还是会造成共享资源的混乱;因此条件变量通常要和互斥锁一起使用,利用互斥锁保证线程同步。

条件变量相关的函数API

#include 
pthread_cond_t cond; // 被条件变量阻塞的线程的线程信息会被记录到这个变量中,以便在解除阻塞的时候使用
// 初始化
int pthread_cond_init(pthread_cond_t *restrict cond,
      const pthread_condattr_t *restrict attr);
// 销毁释放资源        
int pthread_cond_destroy(pthread_cond_t *cond);

// 线程阻塞函数, 哪个线程调用这个函数, 哪个线程就会被阻塞
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);

// 表示的时间是从1971.1.1到某个时间点的时间, 总长度使用秒/纳秒表示
struct timespec {
	time_t tv_sec;      /* Seconds */
	long   tv_nsec;     /* Nanoseconds [0 .. 999999999] */
};
// 将线程阻塞一定的时间长度, 时间到达之后, 线程就解除阻塞了
int pthread_cond_timedwait(pthread_cond_t *restrict cond,
           pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime);

// 唤醒阻塞在条件变量上的线程, 至少有一个被解除阻塞
int pthread_cond_signal(pthread_cond_t *cond);
// 唤醒阻塞在条件变量上的线程, 被阻塞的线程全部解除阻塞
int pthread_cond_broadcast(pthread_cond_t *cond);


链接: https://subingwen.cn/linux/thread-sync/#5-1-%E6%9D%A1%E4%BB%B6%E5%8F%98%E9%87%8F%E5%87%BD%E6%95%B0

生产者与消费者模型的源代码

#include 
#include 
#include 
#include 

#define QUEUEMAX 10 

// 互斥锁与条件变量
pthread_mutex_t mutex;
pthread_cond_t cons_cond;
pthread_cond_t prod_cond;

// 节点
typedef struct node
{
    int num;
    struct node* next;
}Node;

// 指向头结点的指针
Node* head = NULL;
int count = 0;

/* 节点插入方式如下所示
* head-->null
* newNode1
* head-->newNode1-->null
* newNode2
* head-->newNode2-->newNode1-->null
* .....
*/
void* producer(void* arg) {
    // 创建节点
    while (1) {
        Node* newNode = (Node*)malloc(sizeof(Node));
        newNode->num = rand() % 100;
        // 上锁
        pthread_mutex_lock(&mutex);

        // 假如队列已经满,阻塞生产者线程
        // 用while不能用if
        while (count >= 10) {
            pthread_cond_wait(&prod_cond, &mutex);  // 记得唤醒哦
        }

        newNode->next = head;
        head = newNode;
        count++;
        printf("+++Producer  id: %ld, number: %d, count: %d\n", pthread_self(), newNode->num, count);
        // 解锁
        pthread_mutex_unlock(&mutex);

        // 唤醒至少一个阻塞的消费者线程
        pthread_cond_signal(&cons_cond);    

        sleep((unsigned int)rand() % 2);
    }
    return NULL;
}

void* consumer(void* arg) {
    while (1) {
        // 上锁
        pthread_mutex_lock(&mutex);
        // 假如队列已空,应该让消费者线程阻塞
        while (head == NULL || count == 0) {
            pthread_cond_wait(&cons_cond, &mutex); // 要记得唤醒哦!
        }
        Node* curNode = head;
        count--;
        printf("---Consumer  id: %ld, number: %d, count: %d\n", pthread_self(), curNode->num, count);
        head = head->next;
        free(curNode);
        // 解锁
        pthread_mutex_unlock(&mutex);
        pthread_cond_signal(&prod_cond);

        sleep((unsigned int)rand() % 3);
    }
    return NULL;
}

int main()
{
    // 初始化锁和条件变量
    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&cons_cond, NULL);

    // t1是生产者线程数组 t2是消费者线程数组
    pthread_t t1[5], t2[5];

    // 创建线程
    for (int i = 0; i < 5; i++) {
        pthread_create(&t1[i], NULL, producer, NULL);
    }
    for (int i = 0; i < 5; i++) {
        pthread_create(&t2[i], NULL, consumer, NULL);
    }

    // 回收线程
    for (int i = 0; i < 5; i++) {
        pthread_join(t1[i], NULL);
    }
    for (int i = 0; i < 5; i++) {
        pthread_join(t2[i], NULL);
    }

    // 销毁互斥锁和条件变量
    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cons_cond);
    return 0;
}
参考文献:

①《程序员的自我修养》
② https://subingwen.cn/linux/thread-sync/

你可能感兴趣的:(多线程编程,编译,链接,库,条件变量,线程同步,互斥锁,生产者消费者)