进程间同步

进程同步

_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并返回true
  • type __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并返回true
  • type __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

原理解释
在生产者进程中创建多个子线程来互斥的完成生产工作。使用信号量来完成互斥工作。
细节解释
在这里插入图片描述
在这里插入图片描述

18-8-syn-pc-consumer.c

在这里插入图片描述

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在多进程编程中,为了避免多个进程同时访问共享资源而导致的数据不一致或者竞争问题,我们需要使用进程同步的机制。下面是一些常用的进程同步机制及其示例: 1. Lock 锁 Lock 锁是 Python 中最基本的同步原语,它用于控制多个进程对共享资源的访问。在使用 Lock 锁时,只有一个进程能够获取锁,其他进程需要等待锁的释放才能继续执行。下面是一个简单的 Lock 锁示例: ```python import multiprocessing def worker(lock, count): lock.acquire() try: print('Worker {} is working'.format(count)) finally: lock.release() if __name__ == '__main__': lock = multiprocessing.Lock() workers = [] for i in range(10): p = multiprocessing.Process(target=worker, args=(lock, i)) workers.append(p) p.start() ``` 在这个示例中,我们创建了 10 个进程,它们都要对 Lock 锁进行竞争。由于只有一个进程能够获取锁,因此每个进程都需要等待其他进程释放锁后才能继续执行。 2. Semaphore 信号量 Semaphore 信号量是一种更高级的同步原语,它可以控制多个进程对一个资源的访问数量。在使用 Semaphore 信号量时,我们需要指定资源的数量,每次访问资源时会将信号量减 1,如果信号量为 0,则其他进程需要等待资源的释放才能继续执行。下面是一个 Semaphore 信号量示例: ```python import multiprocessing def worker(semaphore, count): semaphore.acquire() try: print('Worker {} is working'.format(count)) finally: semaphore.release() if __name__ == '__main__': semaphore = multiprocessing.Semaphore(3) workers = [] for i in range(10): p = multiprocessing.Process(target=worker, args=(semaphore, i)) workers.append(p) p.start() ``` 在这个示例中,我们创建了 10 个进程,但是只有 3 个进程能够同时访问资源。当有一个进程访问资源时,信号量会减 1,直到信号量为 0 时,其他进程需要等待资源的释放才能继续执行。 3. Event 事件 Event 事件是一种用于多进程通信的同步原语,它可以让一个进程等待另一个进程的信号。在使用 Event 事件时,我们需要创建一个事件对象,当一个进程等待事件时,它会阻塞等待,直到另一个进程触发事件。下面是一个 Event 事件示例: ```python import multiprocessing import time def worker(event, count): event.wait() print('Worker {} is working'.format(count)) if __name__ == '__main__': event = multiprocessing.Event() workers = [] for i in range(10): p = multiprocessing.Process(target=worker, args=(event, i)) workers.append(p) p.start() time.sleep(3) event.set() ``` 在这个示例中,我们创建了 10 个进程,它们都等待事件的触发。当事件被触发时,所有等待事件的进程都会被唤醒,并开始执行。在这个示例中,我们使用 time.sleep() 方法来模拟一个进程在等待事件之前进行一些初始化的操作。然后,我们使用 event.set() 方法来触发事件的发生。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值