进程同步
_sync 原子操作
type __sync_fetch_and_add (type *ptr, type value, ...)
: 将value加到ptr上,结果更新到ptr,并返回操作之前*ptr的值type __sync_fetch_and_sub (type *ptr, type value, ...)
:从ptr减去value,结果更新到ptr,并返回操作之前*ptr的值type __sync_fetch_and_or (type *ptr, type value, ...)
: 将ptr与value相或,结果更新到ptr, 并返回操作之前*ptr的值type __sync_fetch_and_and (type *ptr, type value, ...)
: 将ptr与value相与,结果更新到ptr,并返回操作之前ptr的值
+type __sync_fetch_and_xor (type *ptr, type value, ...)
:将ptr与value异或,结果更新到ptr,并返回操作之前ptr的值type __sync_fetch_and_nand (type *ptr, type value, ...)
: 将ptr取反后,与value相与,结果更新到ptr,并返回操作之前ptr的值
+type __sync_add_and_fetch (type *ptr, type value, ...)
:将value加到ptr上,结果更新到ptr,并返回操作之后新ptr的值type __sync_sub_and_fetch (type *ptr, type value, ...)
:从ptr减去value,结果更新到ptr,并返回操作之后新*ptr的值type __sync_or_and_fetch (type *ptr, type value, ...)
:将ptr与value相或, 结果更新到ptr,并返回操作之后新*ptr的值type __sync_and_and_fetch (type *ptr, type value, ...)
:将ptr与value相与,结果更新到ptr,并返回操作之后新*ptr的值type __sync_xor_and_fetch (type *ptr, type value, ...)
:将ptr与value异或,结果更新到ptr,并返回操作之后新ptr的值
+type __sync_nand_and_fetch (type *ptr, type value, ...)
:将ptr取反后,与value相与,结果更新到ptr,并返回操作之后新ptr的值bool __sync_bool_compare_and_swap (type *ptr, type oldval type newval, ...)
:比较*ptr
与oldval的值,如果两者相等,则将newval更新到*ptr
并返回truetype __sync_val_compare_and_swap (type *ptr, type oldval type newval, ...)
:比较ptr与oldval的值,如果两者相等,则将newval更新到ptr并返回操作之前*ptr的值__sync_synchronize (...)
:发出完整内存栅栏
+type __sync_lock_test_and_set (type *ptr, type value, ...)
: 将value写入ptr,对ptr加锁,并返回操作之前*ptr的值。即,try spinlock语义void __sync_lock_release (type *ptr, ...)
:将0写入到ptr,并对ptr解锁。即,unlock spinlock语义
18-1-syn-fetch-1.c
原理解释
对比
__sync_fetch_and_add()
和__sync_add_and_fetch()
函数的区别
__sync_fetch_and_add()
是先将数据取出来再进行加法操作。
__sync_add_and_fetch()
是先进加法操作再将数据取出来
细节解释
运行结果:
先将数据取出来的话,i=10,这时直接打印出来得到ret=10,再进行加法操作。类似于i++
先进行加法操作得:i=30,这时候打印出来得到ret = 30。类似于++i
18-1-syn-fetch-2.c
原理解释
创建40个子线程对全局变量count进行递增操作,由于是多线程对同一个变量进行操作,所以需要做到同步互斥。
使用__sync_fetch_and_add()
函数来完成递增操作。
原子操作__sync_fetch_and_add()
可使得一次只有一个线程能对同一个共享数据进行操作,因此该函数可以做到多线程同步。
细节解释
运行结果:
18-1-syn-fetch-3.c
原理解释
本程序是18-1-syn-fetch-2.c的一个对比程序,即不使用原子操作
__sync_fetch_and_add()
,直接使用count++来完成多线程递增操作。
count++
会导致一次有多个线程同时对count变量进行操作,最后会导致count变量的值远小于期望值。
细节解释
运行结果:
18-2-syn-compare-test.c
原理解释
验证
bool __sync_bool_compare_and_swap (type *ptr, type oldval type newval, ...)
函数,type __sync_lock_test_and_set (type *ptr, type value, ...)
和void __sync_lock_release (type *ptr, ...)
函数
细节解释
bool __sync_bool_compare_and_swap (type *ptr, type oldval type newval, ...)
:比较*ptr
与oldval的值,如果两者相等,则将newval更新到*ptr
并返回truetype __sync_lock_test_and_set (type *ptr, type value, ...)
: 将value写入*ptr
,对*ptr
加锁,并返回操作之前*ptr
的值。void __sync_lock_release (type *ptr, ...)
:将0写入到*ptr
,并对*ptr
解锁。
运行结果:
互斥锁
Linux提供互斥锁来保护内核中的关键部分。
任务在进入临界区之前调用mutex_lock()函数,并在退出临界区之后调用mutex_unlock()函数。
如果互斥锁资源被占用,则调用mutex_lock()的任务将会进入睡眠状态,在等待队列中等待,并在互斥锁资源的拥有者调用mutex_unlock()时被唤醒。
POSIX所提供的互斥解决方法包括:
- mutex locks
- semaphores
- condition variables
- Non-portable extensions indlude:
- read-wirte
- spinlocks
18-3-syn-pthread-mutex.c
原理解释
两个函数做对比,一个使用了互斥锁,一个没有
细节解释
POSIX信号量
POSIX信号量有两种:有名信号量和无名信号量,无名信号量也被称作基于内存的信号量。有名信号量通过IPC名字进行进程间的同步,而无名信号量如果不是放在进程间的共享内存区中,是不能用来进行进程间同步的,只能用来进行线程同步。(类比管道)
有名信号量
sem_t *sem_open(const char *name, int oflag);
sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value);
函数功能:创建或打开一个信号量
参数:
- name 信号量的名字,标识信号量
- oflag oflag参数可以为:0,O_CREAT,O_EXCL。如果为0表示打开一个已存在的信号量,如果为O_CREAT,表示如果信号量不存在就创建一个信号量,如果存在则打开被返回。此时mode和value需要指定。如果为O_CREAT | O_EXCL,表示如果信号量已存在会返回错误。
- mode 权限位
- value 信号量的初始值
返回值:信号量指针,该指针可供其他对该信号量进行操作的函数使用
int sem_wait(sem_t *sem);
函数功能:将信号量的值减1。如果信号量=0,则sem_wait会阻塞,直到成功使信号量减1
int sem_post(sem_t *sem);
函数功能:将信号量的值加1,也就是释放信号量
int sem_close(sem_t *sem)
函数功能:关闭有名信号量
无名信号量
int sem_init(sem_t *sem, int pshared,unsigned value);
函数功能:初始化指定的信号量
参数:
- sem:信号量变量名
- value:信号量的初始值
- shared:参数pshared用于说明信号量的共享范围,如果pshared为0,那么该信号量只能由初始化这个信号量的进程中的线程使用,如果pshared非零,任何可以访问到这个信号量的进程都可以使用这个信号量。
返回值:
- 成功返回0
- 失败返回错误号
int sem_destroy(sem_t *sem);
函数功能:销毁一个信号量
参数:
- sem:信号量变量名
返回值: - 成功返回0
- 失败返回错误号
int sem_wait(sem_t *sem);
函数功能:将信号量的值减1。如果信号量=0,则sem_wait会阻塞,直到成功使信号量减1
int sem_post(sem_t *sem);
函数功能:将信号量的值加1,也就是释放信号量
18-4-syn-pthread-sem-unamed.c
原理解释
程序大致实现思路与18-3程序相似,只是将同步实现的方式换成无名信号量来实现。
细节解释
运行结果:
18-5-syn-pthread-sem-unamed.c
原理解释
程序大致实现思路与18-3程序相似,只是将同步实现的方式换成有名信号量来实现
细节解释
18-6-syn-pc-con.c
原理解释
这是一个生产者-消费者问题,并且生产者和消费者都是多个的。
生产者将生产好的item放在循环队列中,消费者从循环队列中取出item。
在这里必须做到同步,我们对要生产的item进行编号,一个item只能由一个生产者生产,也只能被一个消费者取走。
实现方法:我们先申请一块共享内存区域,该区域内有一个循环队列,enqueue指向队头,dequeue指向队尾。同时,我们需要记录已经生产了多少item和消费了多少item。当生产的item数量达到要求所有生产者就停止生产,当消费者取出的item达到要求时也退出。
细节解释
18-7-syn-pc-producer.c
原理解释
在生产者进程中创建多个子线程来互斥的完成生产工作。使用信号量来完成互斥工作。
细节解释