Linux系统编程_线程同步

  1. 同步
    线程同步
  2. 互斥量(互斥锁)
    pthread_mutex_init
    pthread_mutex_destroy
    pthread_mutex_lock
    pthread_mutex_unlock
  3. 读写锁
    pthread_rwlock_...
  4. 条件变量
    pthread_cond_...
    pthread_cond_wait
  5. 信号量
    互斥量的升级版,可用于线程间和进程间同步
    sem_...
  6. 进程间同步
    信号量
    文件锁
  7. 哲学家就餐问题
    线程版
    进程版

线程同步

  • 协同步调,按预定先后次序运行。
    关于与时间有关的错误,其产生条件:1)共享数据、2)竞争、3)多个对象间无合理的同步机制。
    引入锁机制。
  • linux系统中,在用户层面的程序中使用到的锁称为“建议锁”:不具有强制性。都应先加锁,再访问共享资源。

互斥量(mutex)

  • 有排他性
  • pthread_mutex_init函数、pthread_mutex_destroy函数
    restrict关键字:所有通过某一指针对所指内存所做的修改只能通过本指针完成。如:pthread_mutex_t *restrict mutex; 表示,所有对mutex所指内存的修改只能通过它修改。
  • pthread_mutex_lock函数(阻塞)、pthread_mutex_unlock函数、pthread_mutex_trylock函数(非阻塞)
    加锁可理解为mutex--,解锁可理解为mutex++
  • 死锁
  1. 情况一:对同一个互斥量加锁两次:因为第二次加锁操作一直在阻塞等待
  2. 情况二:两个线程、两把锁:线程1占有A锁并想获取B锁,而线程2占有B锁并想获取A锁。
  3. 解决办法:当不能获取所有的锁时,放弃已占有的锁。

读写锁

  • 一把锁:以读和写的模式加锁,还可不加锁。
  • 写独占、读共享,写锁优先级高
  • pthread_rwlock_...

条件变量

  • 控制原语:
pthread_cond_t //条件变量类型
pthread_cond_init
pthread_cond_destroy
pthread_cond_wait //重点
pthread_cond_timedwait
pthread_cond_signal
pthread_cond_broadcast
  • 本身不是锁,但会造成线程阻塞,通常与互斥锁配合使用。
  • 生产者消费者模型:条件变量实现
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>

//static initialization, or use pthread_mutex_init() 
// and pthread_cond_init() at the beginning of main function
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t has_product = PTHREAD_COND_INITIALIZER;

struct msg {
    int num;
    struct msg *next;
};

struct msg *head = NULL;
struct msg *mp = NULL;

void *producer(void *arg) {
    for(;;) {
        mp = (struct msg *)malloc(sizeof(struct msg));
        if(mp == NULL) {
            perror("malloc error");
            exit(1);
        }
        mp->num = rand() % 100 + 1; //produces a product
        printf("producer: %lu produces %d\n", pthread_self(), mp->num);

        pthread_mutex_lock(&mutex);
        //add a product to list(simulating the stack)
        mp->next = head;
        head = mp;
        pthread_mutex_unlock(&mutex);

        pthread_cond_signal(&has_product); //unblock at least one thread blocked on has_produt

        sleep(rand() % 5);
    }
}

void *consumer(void *arg) {
    for(;;) {
        pthread_mutex_lock(&mutex);
        while(head == NULL) { //have no product yet; 
                              //can't replace while loop with if condition
                              //'cause there can be more than one consumer
            pthread_cond_wait(&has_product, &mutex); //mutex is released and the thread is blocked 
                                                     // on condition variable has_product
               										 // until another thread calls pthread_cond_signal
              										 // or pthread_cond_broadcast
							 
            //Upon successful return, the mutex shall have been locked
            // and shall be owned by the calling thread.
                        
        }
        //delete a product from list(simulating the stack)
        mp = head;
        head = head->next;
        pthread_mutex_unlock(&mutex);

        printf("---consumer: %lu consumes %d---\n", pthread_self(), mp->num);
        free(mp);

        sleep(rand() % 5);
    }
}

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

    pthread_t ptid, ctid;
        
    pthread_create(&ptid, NULL, producer, NULL);
    pthread_create(&ctid, NULL, consumer, NULL);

    pthread_join(ptid, NULL);
    pthread_join(ctid, NULL);

    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&has_product);

    return 0;
}
  • 线程或者进程阻塞时会释放所占用的cpu资源,提高效率
  • pthread_cond_timedwait函数中的第三个参数“绝对时间”的获取:
 time_t cur = time(NULL); //获取当前绝对时间,相较于1970/1/1 00:00:00(Unix计时元年)
 struct timespec t;
 t.tv_sec = cur + 10; //10秒后
 pthread_cond_timedwait(&cond, &mutex, &t)

信号量

  • 控制原语:
sem_t //信号量类型
sem_init
sem_wait
sem_trywait
sem_timedwait
sem_post
sem_destroy
  • 信号量是进化版的互斥量,其初始化时的初始值是N(相较于互斥量的初始值1)
  • 生产者消费者问题:信号量实现
#include <stdio.h>
#include <unistd.h>
#include <semaphore.h>
#include <stdlib.h>
#include <pthread.h>
#include <time.h>

#define N 5

sem_t sem_blank;
sem_t sem_product;
int num[N] = {0}; //circular queue implemented by global array

void *producer(void *arg) {
        int index = 0;
        while(1) {
                sem_wait(&sem_blank); //decrement sem_blank(if sem_blank is zero, block)
                num[index] = rand() % 10 + 1; //a product is produced
                printf("produce--- %d\n", num[index]);
                sem_post(&sem_product); //increment sem_product and wake up threads blocked on sem_product(if any)

                index = (index + 1) % N;
                sleep(rand() % 3);
        }
        pthread_exit((void *)1);
}

void *consumer(void *arg) {
        int index = 0;
        while(1) {
                sem_wait(&sem_product);
                printf("---consume--- %d\n", num[index]);
                num[index] = 0;
                sem_post(&sem_blank);

                index = (index + 1) % N;
                sleep(rand() % 3);
        }
        pthread_exit((void *)2);

}

int main() {
        pthread_t p_tid, c_tid1, c_tid2;
        srand(time(NULL));

        sem_init(&sem_product, 0, 0);
        sem_init(&sem_blank, 0, N);

        pthread_create(&p_tid, NULL, producer, NULL);
        pthread_create(&c_tid1, NULL, consumer, NULL);
        pthread_create(&c_tid2, NULL, consumer, NULL);

        pthread_join(p_tid, NULL);
        pthread_join(c_tid1, NULL);
        pthread_join(c_tid2, NULL);

        sem_destroy(&sem_product);
        sem_destroy(&sem_blank);

        pthread_exit((void *)0);
}

进程间同步

  • 互斥量:
int pthread_mutexattr_init(pthread_mutexattr_t *attr);
int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared);
int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);
pshared:
  线程锁:PTHREAD_PROCESS_PRIVATE(默认)
  进程锁:PTHREAD_PROCESS_SHARED
pthread_create(pthread_t *tid, pthread_mutexattr_t *attr, ,,)
//示例:父子进程数数,从0开始,父进程每次加1,子进程每次加2,各自均数10次,至30
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/mman.h>
#include <stdlib.h>

struct msg {
        int num;
        pthread_mutex_t mutex;
        pthread_mutexattr_t attr;
};

int main() {
        srand(time(NULL));
        pid_t pid;
        struct msg *pm;
        /*
        int fd = open("mmap.txt", O_RDWR | O_CREAT | O_TRUNC, 0644);
        ftruncate(fd, sizeof(struct msg));
        pm = mmap(NULL, sizeof(*pm), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
        close(fd);
        unlink("mmap.txt");
        */
        pm = mmap(NULL, sizeof(*pm), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
        memset(pm, 0, sizeof(*pm));

        pthread_mutexattr_init(&pm->attr);
        pthread_mutexattr_setpshared(&pm->attr, PTHREAD_PROCESS_SHARED);
        pthread_mutex_init(&pm->mutex, &pm->attr);

        pid = fork();
        if(pid < 0) {
                fprintf(stderr, "fork error");
                exit(1);
        } else if(pid > 0) {
                for(int i = 0; i < 10; i++) {
                        sleep(rand() % 3);
                        pthread_mutex_lock(&pm->mutex);
                        pm->num++;
                        printf("in parent: %d\n", pm->num);
                        pthread_mutex_unlock(&pm->mutex);
                }
        } else if(pid == 0) {
                for(int i = 0; i < 10; i++) {
                        pthread_mutex_lock(&pm->mutex);
                        pm->num += 2;
                        printf("in child: %d\n", pm->num);
                        pthread_mutex_unlock(&pm->mutex);
                        sleep(rand() % 3);
                }
        }

        pthread_mutexattr_destroy(&pm->attr);
        pthread_mutex_destroy(&pm->mutex);

        return 0;
}
  • 文件锁:
    fcntl函数:
    例如:
//加文件锁设置:
struct flock f_lock;
f_lock.l_type = F_RDLCK; //读锁
f_lock.l_type = F_WRLCK; //写锁
f_lock.l_whence = SEEK_SET;
f_lock.l_start = 0;
f_lock.l_len = 0; /* 0表示整个文件加锁*/
fcntl(fd, F_SETLKW, &f_lock); //加锁
//稍后应解锁:
f_lock.l_type = F_UNLCK;
fcntl(fd, F_SETLKW, &f_lock);
//参数1:文件描述符
//参数2:命令F_SETLK、F_SETLKW、F_GETLK
//参数3:struct flock:l_type锁类型、l_whence偏移位置、l_start起始偏移、
//l_len加锁长度(0表示整个文件加锁)、l_pid持有该锁的进程(仅限F_GETLK用)
  • 文件锁应用于进程间通信(类似于线程同步中的读写锁),但不可应用于线程间通信,因为多线程间共享文件描述符,而给文件加锁,是通过修改文件描述符所指向的文件结构体中的成员变量来实现的,因此多线程中无法使用文件锁。

哲学家用餐模型

  • 五个线程、五把锁、一份共享资源、每个人都去拿左手的筷子
  • 死锁:振荡
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值