linux系统编程——同步——互斥锁和条件变量

1. 互斥锁

互斥锁和条件变量 能用于 线程同步
如果 互斥锁 和 条件变量 存放在 共享内存中,还能 实现 进程同步

1.1 初始化互斥锁

       int pthread_mutex_init(pthread_mutex_t *restrict mutex,
           const pthread_mutexattr_t *restrict attr);
       pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
// 静态分配互斥锁
static pthread_mutex_t lock = PTHREAD_MUTEX_INITALIZER;
// 动态分配
pthread_mutex_t *lock2 = malloc(sizeof(*lock2));
phread_mutex_init()

1.2 上锁

       int pthread_mutex_lock(pthread_mutex_t *mutex);
       int pthread_mutex_trylock(pthread_mutex_t *mutex);
       int pthread_mutex_unlock(pthread_mutex_t *mutex);

若资源已经被上锁,

  • 调用 pthread_mutex_trylock,返回 EBUSY
  • 调用 pthread_mutex_lock,被挂起。若多个线程被挂起,锁释放时,内核将唤醒 优先级最高的线程(所以必须设置不同的优先级给线程)

锁数据,不是函数

1.3 示例

生产者消费者问题
使用共享内存之类需要显示同步的情况
linux系统编程——同步——互斥锁和条件变量

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

#define MAXNITEMS               0x0fffffff
#define MAXNTHREADS             100

int nitems;
struct {
        pthread_mutex_t mutex;
        int buf[MAXNITEMS];
        int nput;
        int nval;
} shared = {
        .mutex = PTHREAD_MUTEX_INITIALIZER
};

void *produce(void *arg);
void *consume(void *arg);

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

        if (argc != 3) {
                printf("usage : %s <items> <threads>\n", argv[0]);
                return -1;
        }

        nitems = atoi(argv[1]) > MAXNITEMS ? MAXNITEMS : atoi(argv[1]);
        nthreads = atoi(argv[2]) > MAXNTHREADS ? MAXNTHREADS : atoi(argv[2]);

        for (i = 0; i < nthreads; i++) {
                count[i] = 0;   // 给每个线程一个计数器,记录线程 对共享内存区域操作了多少次
                pthread_create(&tid_produce[i], NULL, produce, &count[i]);
        }

        for (i = 0; i < nthreads; i++) {
                pthread_join(tid_produce[i], NULL);
                printf("count[%d] = %d\n", i, count[i]);
        }

        pthread_create(&tid_consume, NULL, consume, NULL);
        pthread_join(tid_consume, NULL);

        return 0;
}

void *produce(void *arg)
{
        for (;;) {
                pthread_mutex_lock(&shared.mutex);
                if (shared.nput >= nitems) {                    // 所有的区域都写完了,退出
                        pthread_mutex_unlock(&shared.mutex);
                        return NULL;
                }
                shared.buf[shared.nput] = shared.nval;
                shared.nput++;                                          // nput 和 nval 增量都是 1,且初始值都是 0, 所以没有冲突情况下 shared.buf[i] == i
                shared.nval++;
                pthread_mutex_unlock(&shared.mutex);
                *(int *)arg += 1;      // 增加线程操作计数
        }
}

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

        for (i = 0; i < nitems; ++i) {
                if (shared.buf[i] != i) {                       // 若出现冲突,则输出
                        printf("buf[%d] != %d\n", i, shared.buf[i]);
                }
        }
        return NULL;
}

加锁输出
[root@VM-0-12-centos test]# ./a.out 1000000000 5
count[0] = 54572343
count[1] = 53444696
count[2] = 47935356
count[3] = 56164561
count[4] = 56318499
不加锁
[root@localhost test]# ./a.out 10000 4
count[0] = 8596
count[1] = 8192
count[2] = 0
count[3] = 0
buf[8978] != 8977
buf[9152] != 9151

对代码进行修改,让消费者和生产者一起工作

void consume_wait(int i)
{
        for (;;) {
                pthread_mutex_lock(&shared.mutex);
                if (i < shared.nput) {
                        pthread_mutex_unlock(&shared.mutex);
                        return;
                }
                pthread_mutex_unlock(&shared.mutex);
        }
}

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

        for (i = 0; i < nitems; ++i) {
                consume_wait(i);        // 消费 下标为 i 的元素前,检查是否被生产
                if (shared.buf[i] != i) {                       // 若出现冲突,则输出
                        printf("buf[%d] != %d\n", i, shared.buf[i]);
                }
        }
        return NULL;
}

1.4 互斥锁的不足

生产者使用轮询的方式检查条件,浪费cpu,所以希望 能阻塞 直到 条件满足。
这需要条件变量。

linux系统编程——同步——互斥锁和条件变量

上一篇:最佳实践丨用云开发获取、传输微信开放数据


下一篇:Linux系统克隆虚拟机完整步骤分享