系统api-同步与互斥

同步

(1). 场景
同一进程内各个线程间针对该进程内非线程私有数据的访问.各个进程间针对共享内存的访问.

(2). 同步的背景和目标
操作系统的每个进程,或多线程下进程内每个线程有一个自己的执行序列.现代操作系统对同时运行的多个进程,线程的指令按并发方式调度.即可能在多核处理器上同时执行多个线程,进程指令.可能在单核处理器上交叉执行不同线程,进程的指令,而非串行的执行一个进程,线程全部指令序列后再执行下一个指令序列.

基于上述背景,产生了同步需求.同步的目标,首要的是,保证并发执行下的一致性(达到并发交替调度执行和串行调度下执行结果一致),然后要保证的是,在一致性可以保证的情况下,提供尽可能大的并发支持(不要长时间锁定大段指令序列).

互斥锁,条件变量

(1). api

// 互斥锁
int pthread_mutex_lock(pthread_mutex_t*);
int pthread_mutex_trylock(pthread_mutex_t*);
int pthread_mutex_unlock(pthread_mutex_t*);

// 条件变量
// 需要等待时:释放互斥锁,进入睡眠.
// 被唤醒继续时:获取互斥锁,继续运行.
int pthread_cond_wait(pthread_cond_t* cptr, pthread_mutex_t* mptr);

int pthread_cond_timedwait(pthread_cond_t *cptr, pthread_mutex_t *mptr,
	// 指定函数必须返回时的系统时间,如此时还未收到信号,返回ETIMEDOUT.自TUC时间1970.1.1子时,流逝的秒数和纳秒数
	const struct timespec* abstime);
// 唤醒此条件变量上一个等待者
int pthread_cond_signal(pthread_cond_t* cptr);
// 唤醒此条件变量上所有等待者
int pthread_cond_broadcast(pthread_cond_t *cptr);

属性

(1). 互斥锁初始化
a. 常量初始化(获得默认属性)
如:pthread_mutex_t x = PTHREAD_MUTEX_INITIALIZER;
b. 堆上分配形式的初始化
如:pthread_mutex_init(...)
(2). 条件变量初始化
a. 常量初始化(获得默认属性)
如:pthread_cond_t x = PTHREAD_COND_INITIALIZER;
b. 堆上分配形式的初始化
如:pthread_cond_init(...)

int pthread_mutexattr_init(pthread_mutexattr_t* attr);
int pthread_mutexattr_destroy(pthread_mutexattr_t* attr);
int pthread_condattr_init(pthread_condattr_t* attr);
int pthread_condattr_destroy(pthread_condattr_t* attr);
// 在<unistd.h>中定义了_POSIX_PTHREAD_PROCESS_SHARED时才支持,PTHREAD_PROCESS_PRIVATE/PTHREAD_PROCESS_SHARED
int pthread_mutexattr_getpshared(const pthread_mutexattr_t* attr, int* valptr);
int pthread_mutexattr_setpshared(pthread_mutexattr_t* attr, int value);
int pthread_condattr_getpshared(const pthread_condattr_t* attr, int* valptr);
int pthread_condattr_setpshared(pthread_condattr_t *attr, int value);

int pthread_mutex_init(pthread_mutex_t *mptr, const pthread_mutexattr_t *attr);
int pthread_mutex_destroy(pthread_mutex_t *mptr);
int pthread_cond_init(pthread_cond_t *cptr, const pthread_condattr_t *attr);
int pthread_cond_destroy(pthread_cond_t *cptr);

读写锁

(1). api

int pthread_rwlock_rdlock(pthread_rwlock_t* rwptr);
int pthread_rwlock_wrlock(pthread_rwlock_t* rwptr);
int pthread_rwlock_unlock(pthread_rwlock_t* rwptr);

// 不阻塞,无法获取时,返回EBUSY
int pthread_rwlock_tryrdlock(pthread_rwlock_t* rwptr);
int pthread_rwlock_trywrlock(pthread_rwlock_t* rwptr);

属性

(1). 初始化
a. 常量初始化(获得默认属性)
如:pthread_rwlock_t x = PTHREAD_RWLOCK_INITIALIZER;
b. 堆上分配形式的初始化
如:pthread_rwlock_init(...)

int pthread_rwlockattr_init(pthread_rwlockattr_t* attr);
int pthread_rwlockattr_destroy(pthread_rwlockattr_t* attr);
// 在<unistd.h>中定义了_POSIX_PTHREAD_PROCESS_SHARED时才支持,PTHREAD_PROCESS_PRIVATE/PTHREAD_PROCESS_SHARED,
int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *attr, int* valptr);
int pthread_rwlockattr_setpshared(pthread_rwlockattr_t* attr, int value);
int pthread_rwlock_init(pthread_rwlock_t* rwptr, const pthread_rwlockattr_t* attr);
int pthread_rwlock_destroy(pthread_rwlock_t* rwptr);

线程取消

同一进程内线程A可用pthread_cancel取消线程B.(线程A,B属于同一进程)

int pthread_cancel(pthread_t tid);
void pthread_cleanup_push(void (*function)(void*), void* arg);
void pthread_cleanup_pop(int execute);

处理程序在以下情况下被调用:
(1). 线程被同进程另一线程取消
(2). 线程自己调pthread_exit/从线程函数返回
若线程在pthread_cond_wait阻塞期间被进程内另一个线程所取消,调pthread_cancel的线程发出取消请求后即从调用返回
被取消线程,将执行:获取pthread_cond_wait关联的互斥量,然后终止.

故对使用pthread_cond_wait的函数常见的处理模式为

pthread_cleanup_push(清理函数, 参数)
pthread_cond_wait(...);
pthread_cleanup_pop(0);

这样在阻塞于pthread_cond_wait期间,若被取消,则将执行清理函数.清理函数中,可以执行pthread_cond_wait所关联互斥锁的释放及相关工作.若pthread_cond_wait执行下去,则清理函数无存在必要(不需要执行锁的释放),用pthread_cleanup_pop将其删除.

记录上锁

被锁住的文件通过其描述符访问.
用于不同进程间对文件的锁定.记录上锁是一种在共享内存出现前就存在的用于进程间对文件访问进行同步的机制.

int fcntl(int fd, 
	// F_GETLK/F_SETLK/F_SETLKW,cmd为F_GETLK,参数3-flock*是一个值-结果参数
	int cmd, ...);
struct flock {
	// F_RDLCK/F_WRLCK/F_UNLCK
	short l_type;
	// SEEK_SET/SEEK_CUR/SEEK_END
	short l_whence;
	// start offset
	off_t l_start;
	off_t l_len;
	// 返回持有此锁的进程的ID
	pid_t l_pid;
};

描述符关闭时,描述符对应所关联的记录锁会被删除.使用文件记录锁时,要直接用read/write与文件交互.

信号量

(1). 进程可在某个信号量上执行的三种操作
在这里插入图片描述

sem_open,sem_close,sem_unlink

sem_t* sem_open(const char* name, int oflag, ...);
int sem_close(sem_t* sem);
int sem_unlink(const char* name);
int sem_wait(sem_t*);
int sem_trywait(sem_t*);
int sem_post(sem_t*);
// 通过参数2返回参数1所指信号量当前值,如信号量当前已上锁,则返回值为0,或为负数(其绝对值是等待该信号量解锁的线程数)
int sem_getvalue(sem_t*, int*);
int sem_init(sem_t*, int shared, unsigned int value);
int sem_destroy(sem_t*);

(1). 互斥锁&条件变量对比信号量:
a. 互斥锁的一对加锁,解锁必须有同一线程完成.信号量则不必.
b. 条件变量通知pthread_cond_signal发出时,若无因等待对应条件变量而阻塞的线程.则无响应.信号量的资源释放.即使释放时没进程进程/线程阻塞等待此资源.释放操作也会影响到后来资源申请.

互斥锁/信号量进行同步时,一个不可避免的现象是死锁(在未采取死锁避免协议约束下)所以使用时最好引入超时机制.有名信号量具有随内核的持续性.意味着,进程创建/打开某信号量,改变值,后续进程退出.进程所做的更新写入内核信号量对象空间,后续对其他打开此信号量的进程是可见的.

(2). 进程间共享
进程间共享内存信号量/互斥锁/条件变量/读写锁时,同步对象需驻留在由所有希望共享它的进程所共享的内存区中,且对象需以
PTHREAD_PROCESS_SHARED属性初始化/sem_init参数21.有名信号量,不同进程通过同一名字打开的是同一信号量对象.
(3). 限制
一般系统对同时打开的信号量对象数,每个信号量对象最大值有限制

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

raindayinrain

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值