LinuxC:锁、条件变量、信号量实现线程间的同步 生产者与消费者 pthread_mutex_init pthread_cond_init sem_init

本文详细介绍了线程同步中的锁机制,包括互斥锁、自旋锁、递归锁以及细度粒锁的概念与实现,并通过实例展示了如何使用条件变量进行线程间的同步。同时,讨论了死锁产生的条件和避免策略。最后,探讨了信号量在多线程同步中的应用,通过生产者-消费者问题的解决来阐述其工作原理。
摘要由CSDN通过智能技术生成
  • 死锁4个条件:1)互斥条件 2)不可剥夺条件 3)请求和保持条件 4)循环等待条件

  • 管程 :把锁和条件变量联合起来使用。锁实现互斥,条件变量实现同步。同步是协调多个线程运行速度

  • 细度粒锁:减小一个锁所控制的共享数据量

  • 递归锁:可以反复上锁,上锁多少次就要解锁多少次锁才能变为空闲。初始化pthread_mutex_t时将锁的类型设为PTHREAD_MUTEX_RECURSIVE即可设置递归锁

自旋锁

  • test_and_set() : 测试并设置指令。硬件提供 讲某一个位置的值的读取和设置值能成为不可分割的操作
  • 只适用于已知单个线程持有锁时间很短情况,缺点是效率低
struct lock {
    int value;
}

void lock_acquire(struct lock *my_lock) { //获得锁
    while (test_and_set(&my_lock->value))
        ;
    return ;
}

void lock_unlock(struct lock *my_lock) { //解锁
    my_lock->value = 0;
    return ;
}
  • 对换自旋锁. 硬件存储于内存中两个位置的值在一个不可分割的操作中对换
int test = 1;
do {
    swap(&test, &lock);
} while (test);

锁实现线程间的同步

  • mutex变量是非0即1,及可用资源数量
  • 创建锁 及 销毁锁
#include <pthread.h>

//销毁pthread_mutex_init创建的mutex
int pthread_mutex_destroy(pthread_mutex_t *mutex); 

//对mutex初始化,attr为mutex的属性
//成功返回0,失败返回errno
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
            const pthread_mutexattr_t * restrict attr);

//mutex变量是全局变量或static变量,可用以下宏初始化
//以下等价于pthread_mutex_init(mutex, NULL);
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

  • 加锁及解锁. 成功返回0 失败返回errno
#include <pthread.h>

//等待获得mutex锁
int pthread_mutex_lock(pthread_mutex_t *mutex);

//不等待获得mutex锁,若mutex被其他线程用则推出等待立即返回EBUSY
int pthread_mutex_trylock(pthread_mutex_t *mutex);

int pthread_mutex_unlock(pthread_mutex_t *mutex);

  • 死锁

    1)一个线程先后两次调用lock(),导致永远等待被自己占用的锁,陷入死锁

    2)两个线程分别获得了锁后,分别调用对方的锁,导致双方永远挂起状态陷入死锁


条件变量实现线程间的同步

  • 条件变量阻塞等待一个条件,或唤醒等待这个条件的线程
  • 创建条件变量 及 销毁条件变量. 成功返回0,失败返回errno
#include <pthread.h>

int pthread_cond_destroy(pthread_cond_t *cond);
int pthread_cond_init(pthread_cond_t *restrict cond,
              const pthread_condattr_t *restrict attr);

//以下等价于pthread_cond_init(cond, NULL);
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

  • 条件变量操作函数。成功返回0,失败返回errno
#include <pthread.h>

//abstime指定的时刻仍没有线程唤醒当前进程,返回ETIMEOUT
int pthread_cond_timedwait(pthread_cond_t *restrict cond,
                        pthread_mutex_t *restrict mutex,
                const struct timespec *restrict abstime);

//阻塞等待条件变量发生
int pthread_cond_wait(pthread_cond_t *restrict cond,
                     pthread_mutex_t *restrict mutex);

//唤醒条件变量下等待的所有线程
int pthread_cond_broadcast(pthread_cond_t *cond);

//唤醒条件变量下等待的线程
int pthread_cond_signal(pthread_cond_t *cond);
  • 条件变量实现生产者与消费者
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <pthread.h>
pthread_mutex_t headlock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t hadGoods = PTHREAD_COND_INITIALIZER;

typedef struct Goods {
    int data;
    struct Goods *next;
} Goods;
Goods *head = NULL;

void *producer(void *arg) { //生成者
    Goods *ng;
    while (1) {
        ng = (Goods *)malloc(sizeof(Goods));
        ng->data = rand() % 100;

        pthread_mutex_lock(&headlock);
        //头插法
        ng->next = head;
        head = ng;
        pthread_mutex_unlock(&headlock);
        pthread_cond_signal(&hadGoods);

        printf("producer %d\n", ng->data);
        sleep(rand() % 3);
    }
}

void *consumer(void *arg) { //消费者
    Goods *k;
    while (1) {
        pthread_mutex_lock(&headlock);
        if (!head) {
            pthread_cond_wait(&hadGoods, &headlock);
        }
        k = head;
        head = head->next;
        pthread_mutex_unlock(&headlock);

        printf("consumer %d\n", k->data);
        free(k);
        sleep(rand() % 3);

    }
}

int main() {
    srand(time(NULL));

    pthread_t pid, cid;
    pthread_create(&pid, NULL, producer, NULL);
    pthread_create(&cid, NULL, consumer, NULL);

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

    return 0;
}

信号量实现线程间的同步

  • 信号量可用于同一进程间的同步,也可用于不同进程间的同步

    semaphore可用资源数有多个,和mutex不同点在于可以大于1

#include <semaphore.h>

//初始化sem_t变量,value-可用资源数
//pshared=0表示信号量用于同一进程的线程间同步
int sem_init(sem_t *sem, int pshared, unsigned int value);

//获得资源,sem_t值减1,如果减1前值为0则挂起等待
int sem_wait(sem_t *sem); 

//获得资源,sem_t值减1,如果减1前值为0则不会挂起等待
int sem_trywait(sem_t *sem); 

//sem_t值加1,且唤醒挂起等待的线程
int sem_post(sem_t *sem); 

int sem_destroy(sem_t *sem); //释放信号量资源
  • 信号量实现生产者与消费者
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>

int q[5];
sem_t nil_no, goods_no;

void *producer(void *arg) {
    int i = 0;
    while (1) {
        sem_wait(&nil_no);
        q[i] = rand() % 100 + 1;
        printf("producer %d\n", q[i]);
        sem_post(&goods_no);
        i = (i + 1) % 5;
        sleep(rand() % 3);
    }
}

void *consumer(void *arg) {
    int i = 0;
    while (1) {
        sem_wait(&goods_no);
        printf("consumer %d\n", q[i]);
        q[i] = 0;
        sem_post(&nil_no);
        i = (i + 1) % 5;
        sleep(rand() % 3);
    }
}

int main() {
    srand(time(NULL));

    sem_init(&nil_no, 0, 5);
    sem_init(&goods_no, 0, 0);
    //macOS
    //sem_open("nil_no", O_CREAT|O_EXCL, S_IRWXU, 5);
    //sem_open("goods_no", O_CREAT|O_EXCL, S_IRWXU, 0);

    pthread_t pid, cid;
    pthread_create(&pid, NULL, producer, NULL);
    pthread_create(&cid, NULL, consumer, NULL);

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

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值