线程同步、信号量、system v IPC

时间:2023-02-21 21:44:05
一、线程同步
条件变量
什么是条件变量?
线程A等待某个条件成立,条件成立,线程A才继续向下执行。线程B的执行使条件成立,条件成立以后唤醒线程A,以继续执行。这个条件就是条件变量。
pthread_cond_t  类型   就是条件变量的类型
对类型的封装如下:
#include <pthread.h>
//条件变量的静态初始化
pthread_cond_t  cond = PTHREAD_COND_INITIALIZER;

int  pthread_cond_init(pthread_cond_t *cond,\                 pthread_condattr_t *cond_attr);
功能:初始化一个条件变量
参数:
cond:指定要初始化的条件变量
cond_attr:NULL   默认属性
返回值:
0   成功
非0   错误


int  pthread_cond_signal(pthread_cond_t *cond);
功能:启动在等待条件变量变为真的一个线程
参数:
cond:指定条件变量
返回值:
0   成功
非0   错误

int  pthread_cond_broadcast(pthread_cond_t *cond);
功能:启动所有的等待条件变量为真的线程
参数:
cond:指定条件变量
返回值:
0   成功
非0   错误

int  pthread_cond_wait(pthread_cond_t *cond,                 pthread_mutex_t *mutex);
功能:等待条件变量为真
参数:
cond:指定等待的条件变量
mutex:等待之前需要解开的锁
返回值:
0   成功
非0   错误

操作步骤:
1、先原子解mutex锁。
2、让线程等待条件变量变为真。
3、条件变量为真的时候,加锁

int pthread_cond_timedwait(pthread_cond_t *cond,
                   pthread_mutex_t  *mutex, \
             const struct timespec *abstime);
功能:超时等待,超时返回错误
参数:
cond:指定等待的条件变量
mutex:等待之前需要解开的锁
abstime:指定等待的时间
返回值:
0   成功
非0   错误
int  pthread_cond_destroy(pthread_cond_t *cond);
功能:销毁条件变量
参数:
cond:指定要销毁的条件变量
返回值:
0   成功
非0   错误

生产者和消费者的例子
链表实现    生产者生产出来对象放到链表的头部
            消费者从链表的头部取出消费。
第一思考:两个线程如何同步访问链表的头部
第二思考:如果链表为空,消费者等待生产者线程生产对象。
第三思考:生产者线程,生产出对象通知消费者。

代码参见: cond.c


信号量
多个线程使用多个资源的时候,使用信号量实现线程的同步。
sem_t类型 
类型的操作如下:
sem_init(3)
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
功能:初始化信号量
参数:
sem:指定了要初始化的信号量
pshared:0   应用于多线程   非0   多进程
value:指定了信号量的初始值
返回值:
0  成功
-1   失败  errno被设置

sem_destroy(3)
#include <semaphore.h>
int sem_destroy(sem_t *sem);
功能:销毁信号量
参数:
sem:指定要销毁的信号量
返回值:
0  成功
-1  错误  errno被设置

sem_post(3)
#include <semaphore.h>
int sem_post(sem_t *sem);
功能:信号量的值加1操作
参数:
sem:指定的信号量,就是这个信号量的值加1.
返回值:
0  成功
-1   错误  errno被设置

sem_wait(3)
#include <semaphore.h>
int sem_wait(sem_t *sem);
功能:信号量的值减1.如果信号量的值为0.阻塞等待
参数:
sem:指定了要操作的信号量
返回值:
0  成功
-1   错误   errno被设置
int sem_trywait(sem_t *sem);

int sem_timedwait(sem_t *sem, \
    const struct timespec *abs_timeout);


使用信号量,实现生产者和消费者的例子
使用环状队列实现。
代码参见   sem.c

线程结束了

二、system v IPC
信号量集
信号量集就是数组,数组里的每一个元素都是信号量类型的。

1、先获取键值
ftok(3)
2、使用键值获取信号量集的id
semget(2)
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t  key,  int  nsems, int semflg);
功能:获取信号量集的id
参数:
key:ftok(3)的返回值
nsems:指定了信号量集中的元素个数
semflg:
IPC_CREAT:
IPC_EXCL:
mode:指定信号量集的权限
返回值:
非负整数,是信号量集的id。
-1   错误   errno被设置

3、设置信号量集中的信号量的初值或者获取信号量的值。
semctl(2)
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd, ...);
功能:信号量的控制操作
参数:
semid:指定了信号量集
semnum:指定了信号量在数组中的下标
cmd:指定了对信号量的控制操作命令
GETVAL:获取到第几个信号了的semval值。  semval的值
SETVAL:设置第几个信号量的semval值为arg.val。 0

...:可变参数。这个有没有,什么类型?取决于cmd。
返回值:
-1   错误  errno被设置

需要自定义
union semun {
    int        val;    /* Value for SETVAL */
    struct semid_ds *buf; /* Buffer for IPC_STAT,IPC_SET */
    unsigned short  *array; /*Array for GETALL, SETALL */
    struct seminfo  *__buf;  /* Buffer for IPC_INFO
                                 (Linux-specific) */
};

4、对指定的信号量加法或者减法操作
semop(2)
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semop(int semid, struct sembuf *sops, unsigned nsops);
功能:信号量操作
参数:
semid:指定了信号量集
sops:指定了对某个信号量的具体操作
nsops:指定了要操作的信号量的个数。

返回值:
0   成功
-1  错误  errno被设置

操作定义在这个结构体中
struct sembuf{
    unsigned short sem_num;  /* semaphore number */
        short          sem_op;   /* semaphore operation */
        short          sem_flg;  /* operation flags */

};
sem_flg:
IPC_NOWAIT  
IPC_UNDONE  自动撤销操作

sem_num:指定了要操作的信号量在数组的下标
sem_op:指定了操作的类型
操作类型分为3种
>0   semval+sem_op  semval做加法
=0   

<0:
semval>sem_op的绝对值,这个操作立即执行。

semval<sem_op的绝对值
申请的资源数>可用资源数   IPC_NOWAIT  非阻塞
             sem_flg=0   阻塞等待资源可用

举例说明   使用信号量集实现进程间的通讯。
两个进程
PA.c    设置信号量的值 ,每隔3秒,semval的值减1 
Pb.c    每隔1秒,获取信号量的值,