1.假设p1先执行,执行到p(s), s-=1, 此时s=-1<0,进程阻塞,主动放弃cpu使用权,cpu调度执行p2,执行p2的具体任务,然后进行v(s),,s+=1,s=0,p2执行完毕。cpu调度继续执行p1,此时s=0,p1被唤醒,因此就达到了先执行p2后执行p1的同步关系。 2.假设p2先执行,首先执行具体的代码,然后进行v(s),s+=1,s=1>0,然后p2执行完毕。cpu调度执行p1,p1首先p(s),s-=1,s=0,然后执行具体的代码。同样也达到了先执行p2后执行p1的同步关系。 二.Linux下信号量实现同步,线程2先执行输出"hello",线程1后执行输出"world\n"的功能
大家好,又见面了,我是你们的朋友全栈君。 引子 在编译2.6内核的时候,你会在编译选项中看到[*] Enable futex support这一项,上网查,有的资料会告诉你”不选这个内核不一定能正确的运行使用glibc的程序”,那futex是什么?和glibc又有什么关系呢? 1. 什么是Futex Futex 是Fast Userspace muTexes的缩写,由Hubertus Franke, Matthew Kirkwood, Ingo Molnar and Rusty Russell共同设计完成。几位都是linux领域的专家,其中可能Ingo Molnar大家更熟悉一些,毕竟是O(1)调度器和CFS的实现者。 Futex按英文翻译过来就是快速用户空间互斥体。其设计思想其实 不难理解,在传统的Unix系统中,System V IPC(inter process communication),如 semaphores, msgqueues, sockets还有文件锁机制(flock())等进程间同步机制都是对一个内核对象操作来完成的,这个内核对象对要同步的进程都是可见的,其提供了共享 的状态信息和原子操作。当进程间要同步的时候必须要通过系统调用(如semop())在内核中完成。可是经研究发现,很多同步是无竞争的,即某个进程进入 互斥区,到再从某个互斥区出来这段时间,常常是没有进程也要进这个互斥区或者请求同一同步变量的。但是在这种情况下,这个进程也要陷入内核去看看有没有人 和它竞争,退出的时侯还要陷入内核去看看有没有进程等待在同一同步变量上。这些不必要的系统调用(或者说内核陷入)造成了大量的性能开销。为了解决这个问 题,Futex就应运而生,Futex是一种用户态和内核态混合的同步机制。首先,同步的进程间通过mmap共享一段内存,futex变量就位于这段共享 的内存中且操作是原子的,当进程尝试进入互斥区或者退出互斥区的时候,先去查看共享内存中的futex变量,如果没有竞争发生,则只修改futex,而不 用再执行系统调用了。当通过访问futex变量告诉进程有竞争发生,则还是得执行系统调用去完成相应的处理(wait 或者 wake up)。简单的说,futex就是通过在用户态的检查,(motivation)如果了解到没有竞争就不用陷入内核了,大大提高了low-contention时候的效率。 Linux从2.5.7开始支持Futex。 2. Futex系统调用 Futex是一种用户态和内核态混合机制,所以需要两个部分合作完成,linux上提供了sys_futex系统调用,对进程竞争情况下的同步处理提供支持。 其原型和系统调用号为 #include <linux/futex.h> #include <sys/time.h> int futex (int *uaddr, int op, int val, const struct timespec *timeout,int *uaddr2, int val3); #define __NR_futex 240 虽然参数有点长,其实常用的就是前面三个,后面的timeout大家都能理解,其他的也常被ignore。 uaddr就是用户态下共享内存的地址,里面存放的是一个对齐的整型计数器。 op存放着操作类型。定义的有5中,这里我简单的介绍一下两种,剩下的感兴趣的自己去man futex FUTEX_WAIT: 原子性的检查uaddr中计数器的值是否为val,如果是则让进程休眠,直到FUTEX_WAKE或者超时(time-out)。也就是把进程挂到uaddr相对应的等待队列上去。 FUTEX_WAKE: 最多唤醒val个等待在uaddr上进程。 可见FUTEX_WAIT和FUTEX_WAKE只是用来挂起或者唤醒进程,当然这部分工作也只能在内核态下完成。有些人尝试着直接使用futex系统调 用来实现进程同步,并寄希望获得futex的性能优势,这是有问题的。应该区分futex同步机制和futex系统调用。futex同步机制还包括用户态 下的操作,我们将在下节提到。 3. Futex同步机制 所有的futex同步操作都应该从用户空间开始,首先创建一个futex同步变量,也就是位于共享内存的一个整型计数器。 当 进程尝试持有锁或者要进入互斥区的时候,对futex执行”down”操作,即原子性的给futex同步变量减1。如果同步变量变为0,则没有竞争发生, 进程照常执行。如果同步变量是个负数,则意味着有竞争发生,需要调用futex系统调用的futex_wait操作休眠当前进程。 当进程释放锁或 者要离开互斥区的时候,对futex进行”up”操作,
文章主要介绍了在Linux系统中,如何利用自旋锁来实现线程之间的同步和互斥。主要包括了自旋锁的定义、工作原理、使用方式和注意事项,并通过实例介绍了如何在C语言中实现自旋锁。
引入线程是为了更为精细粒度的分配CPU时间片,节省系统公共资源,更为充分和有效的配置有限运算能力
信号量(semaphore)本质上是一个计数器,用于多进程对共享数据对象的读取,它和管道有所不同,它不以传送数据为主要目的,它主要是用来保护共享资源(信号量也属于临界资源),使得资源在一个时刻只有一个进程独享。 在信号量进行PV操作时都为原子操作(因为它需要保护临界资源)。
生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。这个阻塞队列就是用来给生产者和消费者解耦的。
项目中遇到一个bug,因为接入了几家越狱平台:91、同步推、PP助手,在设备上安装了三个应用,启用其中任意一个,另外二个启动后无法创建发送socket消息,从而导致游戏直接死在登录那里,再次点击登录时线程才会被唤醒(无法发送的原因定位到,是因为在调用sem_post方法后无法将线程唤醒)。之后我尝试将信号量改为条件变量,就再也没有遇到那个问题了。具体改写的几个方法:
进程与线程之间是有区别的,不过linux内核只提供了轻量进程的支持,未实现线程模型。Linux是一种“多进程单线程”的操作系统。Linux本身只有进程的概念,而其所谓的“线程”本质上在内核里仍然是进程。
想必各位读者在看了昨天的文章分享之后,大概对线程有了一个比较清楚的认识了,但是昨天讲的东西过于纯理论化,所以在昨天的基础上,今天我们就来进行实战演练,做到活学活用,废话不多说,直接开干吧。
信号量是一种计数器,用来控制对多个进程/线程共享的资源进行访问。常和锁一同使用。 在某个进程/线程正在对某个资源进行访问时,信号量可以阻止另一个进程/线程去打扰。 生产者和消费者模型是信号量的典型使用。
信号量强调的是线程(或进程)间的同步:“信号量用在多线程多任务同步的,一个线程完成了某一个动作就通过信号量告诉别的线程,别的线程再进行某些动作(大家都在sem_wait的时候,就阻塞在那里)。当信号量为单值信号量时,也可以完成一个资源的互斥访问。信号量测重于访问者对资源的有序访问,在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源。
总结 Postgresql使用匿名信号量完成进程间的一些同步操作。 匿名信号量由父进程创建在mmap的共享内存内,通过血缘关系继承给子进程,子进程从共享内存中获取信号量数据结构直接使用即可。 Postgresql的信号量分配比较简单,每一个进程拥有一个自己的信号量。初始化后值为1,表示未锁定状态。 加锁后信号量=0。 解锁后信号量=1。 Postgresql的信号量初始化使用的是POSIX接口(SYSTEM V)中的匿名信号量(命名信号量)。 struct PGPROC { ... PGSem
---- Hello、Hello大家好,我是木荣,今天我们继续来聊一聊Linux中多线程编程中的重要知识点,详细谈谈多线程中同步和互斥机制。 同步和互斥 互斥:多线程中互斥是指多个线程访问同一资源时同时只允许一个线程对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的; 同步:多线程同步是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源
但是在libc库中,函数sem_open、sem_close、sem_unlink只有声明,并未实现。
本文介绍了Linux信号量、POSIX信号量、Linux条件变量和Linux线程同步基本概念,并通过代码示例展示了如何使用这些技术进行线程同步。
https://www.memsql.com/blog/linux-off-cpu-investigation/
一、功能上的区别 posix和system v有什么区别/?现在在应用时应用那一标准 浮云484212 | 浏览 243 次 2014-11-06 10:36 2014-11-19 22:36 最佳答案 它们是有关信号量的两组程序设计接口函数。POSIX信号量来源于POSIX技术规范的实时扩展方案(POSIX Realtime Extension),常用于线程;system v信号量,常用于进程的同步。这两者非常相近,但它们使用的函数调用各不相同。前一种的头文件为semaphore.h,函数调用为sem_
之前看过一段时间Android property源码,发现其中对共享内存的使用方式挺优秀,对于实际的工作开发也有借鉴意义。于是乎参考其设计思想,个人用C++也完成一篇共享内存的使用代码。共享内存的使用可参考进程间通信 共享内存, 本篇记录一种比较优雅的共享内存使用方式。
上面的代码很简单,就是启动一个线程,然后先线程里循环打印字段字符串。我们就以这个最简单的例子来开口。
在单线程的程序里,有两种基本的数据:全局变量和局部变量。但在多线程程序里,还有第三种数据类型:线程数据(TSD: Thread-Specific Data)。
线程 为什么使用线程? 使用fork创建进程以执行新的任务,该方式的代价很高——子进程将父进程的所有资源都复制一遍。 多个进程之间不会直接共享内存。 进程是系统分配资源的基本单位,线程是进程的基本执行
答案是用信号量,当然是在线程不多的情况,每个线程分配一个信号量,然后在前面的线程先获取自己的信号量,结束后释放下一个线程的信号量,从而达到有序
线程(thread)技术早在60年代就被提出,但真正应用多线程到操作系统中去,是在80年代中期,solaris是这方面的佼佼者。传统的Unix也支持线程的概念,但是在一个进程(process)中只允许有一个线程,这样多线程就意味着多进程。现在,多线程技术已经被许多操作系统所支持,包括Windows/NT,当然,也包括Linux。 为什么有了进程的概念后,还要再引入线程呢?使用多线程到底有哪些好处?什么的系统应该选用多线程?我们首先必须回答这些问题。 使用多线程的理由之一是和进程相比,它是一种非常”节俭”的多任务操作方式。我们知道,在Linux系统下,启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这是一种”昂贵”的多任务工作方式。而运行于一个进程中的多个线程,它们彼此之间使用相同的地址空间,共享大部分数据,启动一个线程所花费的空间远远小于启动一个进程所花费的空间,而且,线程间彼此切换所需的时间也远远小于进程间切换所需要的时间。据统计,总的说来,一个进程的开销大约是一个线程开销的30倍左右,当然,在具体的系统上,这个数据可能会有较大的区别。 使用多线程的理由之二是线程间方便的通信机制。对不同进程来说,它们具有独立的数据空间,要进行数据的传递只能通过通信的方式进行,这种方式不仅费时,而且很不方便。线程则不然,由于同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其它线程所用,这不仅快捷,而且方便。当然,数据的共享也带来其他一些问题,有的变量不能同时被两个线程所修改,有的子程序中声明为static的数据更有可能给多线程程序带来灾难性的打击,这些正是编写多线程程序时最需要注意的地方。 除了以上所说的优点外,不和进程比较,多线程程序作为一种多任务、并发的工作方式,当然有以下的优点: 1) 提高应用程序响应。这对图形界面的程序尤其有意义,当一个操作耗时很长时,整个系统都会等待这个操作,此时程序不会响应键盘、鼠标、菜单的操作,而使用多线程技术,将耗时长的操作(time consuming)置于一个新的线程,可以避免这种尴尬的情况。 2) 使多CPU系统更加有效。操作系统会保证当线程数不大于CPU数目时,不同的线程运行于不同的CPU上。 3) 改善程序结构。一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独立的运行部分,这样的程序会利于理解和修改。 下面我们先来尝试编写一个简单的多线程程序。
信号量(sem)在操作系统中是一种实现系统中任务与任务、任务与中断间同步或者临界资源互斥保护的机制。在多任务系统中,各任务之间常需要同步或互斥,信号量就可以为用户提供这方面的支持。
一 线程间同步 同步:相互之间配合完成一件事情 互斥:保证访问共享资源的完整性(有你没我) POSIX 线程中同步:使用信号量实现 信号量 : 表示一类资源,它的值表示资源的个数 对资源访问: p操作(申请资源) [将资源的值 - 1] .... V操作(释放资源) [将资源的值 + 1] 1.定义信号量 sem_t sem ; 2.初始化信号量 int sem_init(sem_t *sem, int pshared, unsigned int value); 参数: @sem 信号量 @pshared 0:线程间使用 @value 初始化的信号量的值 返回值: 成功返回0,失败返回-1 3.P操作 int sem_wait(sem_t *sem); 4.V操作 int sem_post(sem_t *sem); 二 进程间通信(进程间数据交互) (1)传统进程间通信方式 [1]无名管道 [2]有名管道 [3]信号 (2)System 5 IPC对象进程间通信方式 [1]消息队列 [2]共享内存 [3]信号灯集 (3)socket通信 (4)Android系统中增加Binder进程间通信方式 Linux 支持以上所有进程间通信方式 三 管道进程间通信 (1)无名管道 特点: 只能用于具有亲缘关系进程间通信(具有亲缘关系的进程具有数据拷贝动作(复制父进程创建子进程)) int pipe(int pipefd[2]); 功能:创建一个无名管道 参数: @pipefd 获取操作无名管道的文件描述符 pipefd[0]:读无名管道 pipefd[1]:写无名管道 返回值: 成功返回0,失败返回-1 (2)管道读写规则 读端存在 ,写管道 ---->只要管道没有满,都可以写入数据到管道 读端不存在,写管道 ---->此时写管道没有意义,操作系统会发送SIGPIPE杀死写管道的进程 写端存在, 读管道 ---->此时管道中读取数据,管道中没有数据,读阻塞 写端不存在,读管道 ---->此时管道中读取数据,管道中没有数据,此时不阻塞,立即返回,返回值0 (3)有名管道 特点:可以用于任意进程间通信,它是一种特殊的文件,在文件系统存在名字, 而文件中存放的数据是在内核空间,而不是在磁盘上 1.创建一个有名管道文件 int mkfifo(const char *pathname, mode_t mode); @pathname 有名管道存在的路径 @mode 有名管道的权限 返回值: 成功返回0,失败返回-1 2.打开有名管道文件 open 如果有名管道的一端以只读的方式打开,会阻塞,直到另一端以写(只写或读写)的方式打开 如果有名管道的一端以只写的方式打开,会阻塞,直到另一端以读(只读或读写)的方式打开 3.读写操作 read /write 4.关闭管道文件 close(fd); 四 信号 信号是异步进程间通信方式 进程对信号的响应方式: <1>忽略 SIGKILL 和 SIGSTOP 不能忽略 <2>捕捉 当进程收到信号,此时执行的信号处理函数 <3>默认 大部分信号对进程的默认操作方式都是杀死进程 子进程状态发生改变的时候,操作系统向父进程发送SIGCHLD,默认对它处理方式是忽略 typedef void (*sighandler_t)(int); sighandler_t signal(int signum, sighandler_t handler); 功能:设置进程对信号处理方式 参数: @signum 信号的编号 @handler SIG_IGN : 忽略信号 SIG_DFL : 使用默认处理方式 函数名 : 捕捉方式处理 返回值: 成功返回handler,失败返回SIG_ERR 练习: 如何进行不阻塞,不轮训方式回收僵尸态子进程 2.在进程中设置一个定时器 unsigned int alarm(unsigned int seconds); 参数: @seconds 定时的时间,以秒为单位 注意: 一旦定时时间完成,操作系统就会向进程发送SIGALRM信号 A进程: 读文件,写管道 A进程结束条件:文件没有数据可读 B进程: 读管道,写文件 B进程结束条件:在
__pshared 不为0时此信号量在进程间共享,否则只能为当前进程的所有线程共享
信号灯概述 什么是信号灯 信号灯用来实现同步,用于多线程,多进程之间同步共享资源(临界资源)。 PV原语:信号灯使用PV原语 P原语操作的动作是: u sem减1。 u sem减1后仍大于或等于零,则进程继续执行。 u 若sem减1后小于零,则该进程被阻塞后进入与该信号相对应的队列中,然后转进程调度。 V原语操作的动作是: u sem加1。 u 若相加结果大于零,则进程继续执行。 u 若相加结果小于或等于零,则从该信号的等待队列中唤醒一等待进程,然后再返回原进程继续执行或转进程调度。 信号灯分类 按信号灯实
如题,应届生除了要良好地掌握算法和数据结构以外,以下一些技能点列表希望对大家有帮助,有兴趣的朋友可以参考这个针对性地补缺补差。文章列出的技能点有的要求熟悉,有的了解即可,注意技能点前面的修饰词。如果没有明确给出“熟悉”“了解”等字眼,要求均为熟悉。 一、操作系统方面 多线程相关与线程之间同步技术 熟练使用(但不局限于)以下linux API linux下的线程创建、等待、获取线程id 1int pthread_create(pthread_t *thread, const pthread_attr_t *
最近由于session数量增加,需要调整session,也就是要调整process参数。看是比较简单的一个问题,却遭遇了ORA-27300,ORA-27301。因为这个涉及到了有关内核参数kernel.sem的修改。下面是其具体描述。 1、故障现象 OS版本:SUSE Linux Enterprise Server 10 SP3 (x86_64) - Kernel \r (\l) DB版本: SQL*Plus: Release 10.2.0.3.0 - Production 需求:Processes参数由1000修改到2000 SQL> startup pfile=/u02/database/MRDB/initMRDB.ora; ORA-27154: post/wait create failed ORA-27300: OS system dependent operation:semget failed with status: 28 ORA-27301: OS failure message: No space left on device ORA-27302: failure occurred at: sskgpcreates 2、故障的分析与解决 #起初咋一看还以为空间不够呢,如下,显然不是空间的问题,其次有个很重要的表示"semget" SQL> ho df -h Filesystem Size Used Avail Use% Mounted on /dev/sda3 1.8T 826G 885G 49% / udev 32G 116K 32G 1% /dev /dev/sda1 99M 9.5M 85M 11% /boot #后台日志如下 Tue Aug 5 18:07:22 2014 Starting ORACLE instance (normal) Tue Aug 5 18:07:22 2014 Errors in file /u02/database/MRDB/udump/mrdb_ora_30366.trc: ORA-27154: post/wait create failed ORA-27300: OS system dependent operation:semget failed with status: 28 ORA-27301: OS failure message: No space left on device ORA-27302: failure occurred at: sskgpcreates #Google了一些文章,描述的是需要调整内核参数kernel.sem,也就是信号量的问题 v2012db02u:~ # grep kernel.sem /etc/sysctl.conf kernel.sem = 1250 32000 100 256 #这个是当前的值 信号量设置示例 SEMMSL应该设置为服务器中实例中具有最大的PROCESSES参数+10,例如,当最大的PROCESSES参数为5000时,SEMMSL应设置为5010。 SEMMNS参数应设置为SEMMSL*SEMMNI,接上例SEMMSL为5010,SEMMNI的值一般为128,则SEMMNS参数应为(5010*128)=641280。 SEMOPM参数应设置与SEMMSL参数相同,接上例此处应设置为5010 因此对于信号量建议做如下设置 sysctl -w kernel.sem="5010 641280 5010 128" #关于这个参数的具体描述及设置可以参考:Linux 内核参数优化(for oracle) #由于当前服务器存在N个实例,因此给了一个比较大的值,生产环境应慎重修改,如下 v2012db02u:~ # vi /etc/sysctl.conf v2012db02u:~ # sysctl -p ........... kernel.sem = 7000 1792000 7000 256 ........... #再次启动,狂汗.... SQL> startup pfile=/u02/database/MR/initMR.ora; ORA-00064: object is too large to allocate on this O/S (1,5150880) v2012db02u:~ > oerr ora 00064 00064, 00000, "object is too large to allocate on this O/S (%s,%s
实际项目中,我们希望修改了配置文件后,但又不想通过重启进程让它重新加载配置文件,可以使用signal的方式进行信号传递,或者我们希望通过信号控制,实现一种优雅的退出方式。Golang为我们提供了signal包,实现信号处理机制,允许Go 程序与传入的信号进行交互。
前一段时间由于开题的事情一直耽搁了我搞Linux的进度,搞的我之前学的东西都遗忘了,非常烦躁的说,如今抽个时间把之前所学的做个小节。文章内容主要总结于《Linux程序设计第3版》。
信号灯、互斥锁和条件变量之间的差异: 互斥锁必须由给他上锁的线程解锁,信号灯的挂出不必由执行过它的等待操作的同一线程执行 互斥锁要么被锁住要么被解开 既然信号灯有一个与之相关连的状态(它的计数值),信号灯挂出操作总是被记住。然而当想一个条件变量发送信号时,如果没有线程等待在该条件变量上,那么该信号将丢失
该文章介绍了CPU缓存以及多线程程序中CPU缓存一致性的问题,并给出了具体的例子和解决方案。文章指出,多线程程序中的CPU缓存不一致问题可能会导致性能下降,因此需要谨慎处理。通过使用原子操作、锁操作等技术,可以避免CPU缓存不一致问题,从而提高程序的性能。
最后运行的结果不是固定的,有可能是0、-1,如果有这个ticket_num变量代表是库存的话,那么就会出现库存为负数的情况,所以需要引入线程同步来保证线程安全。
工作函数 uv__fs_work 做的事情很简答 根据要做的操作类型做对应的操作函数调用,uv__fs_read 根据系统类型采取合适的读取函数,可以简单的视为调用read从fd读数据即可;
/* Semaphores a la POSIX 1003.1b */ #include "pthread.h" #include "semaphore.h" #include "internals.h" #include "restart.h" #ifndef HAS_COMPARE_AND_SWAP /* If we have no atomic compare and swap, fake it using an extra spinlock. */ #include "spinlock.h
Linux互斥与同步 零、前言 一、Linux线程互斥 1、基本概念及引入 2、互斥量mutex介绍 3、互斥量的使用 4、互斥量原理 二、可重入/线程安全 1、基本概念 2、线程安全 3、重入函数 4、联系与区别 三、常见锁概念 四、Linux线程同步 1、基本概念 2、条件变量的使用 3、条件变量等待 4、条件变量使用规范 五、POSIX信号量 1、信号量概念及介绍 2、信号量的使用 零、前言 本章主要讲解学习Linux中对多线程的执行中的同步与互斥 一、Linux线程互斥 1、基本概念及引入 互
生产者消费者模型(CP模型)是一种非常经典的设计,常常出现在各种 「操作系统」 书籍中,深受教师们的喜爱;这种模型在实际开发中还被广泛使用,因为它在多线程场景中是十分高效的!
1 编译运行附件中的代码,提交运行结果截图,并说明程序功能 2 修改代码,把同步资源个数减少为3个,把使用资源的线程增加到 (你的学号%3 + 4)个,编译代码,提交修改后的代码和运行结果截图。
几种进程间的通信方式:管道,FIFO,消息队列,他们的共同特点就是通过内核来进行通信(假设POSIX消息队列也是在内核中实现的,因为POSIX标准并没有限定它的实现方式)。向管道,FIFO,消息队列写入数据需要把数据从进程复制到内核,从这些IPC读取数据的时候又需要把数据从内核复制到进程。所以这种IPC方式往往需要2次在进程和内核之间进行数据的复制,即进程间的通信必须借助内核来传递。如下图所示:
一般 Android 系统相机的最高帧率在 30 FPS 左右,当帧率低于 20 FPS 时,用户可以明显感觉到相机画面卡顿和延迟。
开发成长之路(1)-- C语言从入门到开发(入门篇一) 开发成长之路(2)-- C语言从入门到开发(函数与定制输入输出控制函数) 开发成长之路(3)-- C语言从入门到开发(讲明白指针和引用,链表很难吗?) 开发成长之路(4)-- C语言从入门到开发(距离开发,还差这一篇) 开发成长之路(5)-- C语言从入门到开发(仿ATM机项目,我写的第一个项目) 开发成长之路(6)-- C++从入门到开发(C++入门不难) 开发成长之路(6)-- C++从入门到开发(C++知名库:STL入门·容器(一)) 开发成长之路(7)-- C++从入门到开发(C++知名库:STL入门·容器(二)) 开发成长之路(8)-- C++从入门到开发(C++知名库:STL入门·容器(三)) 开发成长之路(9)-- C++从入门到开发(C++知名库:STL入门·空间配置器) 开发成长之路(10)-- C++从入门到开发(C++知名库:STL入门·算法) 开发成长之路(11)-- STL常用函数大集合 开发成长之路(12)-- Linux网络服务端编程(通识篇之熟悉操作环境) 开发成长之路(13)-- Linux网络服务端编程(通识篇)
1、概念 futex: a sort of fast, user-space mutual exclusion primitive. Futex是一种用户态和内核态混合的同步机制。首先,同步的进程间通过mmap共享一段内存,futex变量就位于这段共享的内存中且操作是原子的,当进程尝试进入互斥区或者退出互斥区的时候,先去查看共享内存中的futex变量,如果没有竞争发生,则只修改futex,而不用再执行系统调用了。当通过访问futex变量告诉进程有竞争发生,则还是得执行系统调用去完成相应的处理(wait 或者 wake up)。简单的说,futex就是通过在用户态的检查,(motivation)如果了解到没有竞争就不用陷入内核了,大大提高了low-contention时候的效率。 https://lwn.net/Articles/172149/ https://lwn.net/Articles/360699/ 2、futex的由来 为什么要有futex,他解决什么问题?何时加入内核的?我们来看下 经研究发现,很多同步是无竞争的,即某个进程进入互斥区,到再从某个互斥区出来这段时间,常常是没有进程也要进这个互斥区或者请求同一同步变量的。但是在这种情况下,这个进程也要陷入内核去看看有没有人和它竞争,退出的时侯还要陷入内核去看看有没有进程等待在同一同步变量上。这些不必要的系统调用(或者说内核陷入)造成了大量的性能开销。为了解决这个问题,Futex就应运而生。 前面的概念已经说了,futex是一种用户态和内核态混合同步机制,为什么会是用户态+内核态,听起来有点复杂,由于我们应用程序很多场景下多线程都是非竞争的,也就是说多任务在同一时刻同时操作临界区的概率是比较小的,大多数情况是没有竞争的,在早期内核同步互斥操作必须要进入内核态,由内核来提供同步机制,这就导致在非竞争的情况下,互斥操作扔要通过系统调用进入内核态。 我们来看一下程序 程序1: pthread_mutex_t lock; int count = 0; void thread1() { while(1) { pthread_mutex_lock(&lock); /* do something */ count++; pthread_mutex_unlock(&lock); } } void thread2() { while(1) { sleep(60); pthread_mutex_lock(&lock); count = 0; pthread_mutex_unlock(&lock); } } pthread_create(&tid1, NULL, thread1, NULL); pthread_create(&tid2, NULL, thread1, NULL);
System-V的信号量是老古董,除非万不得已,否则我们一般用POSIX信号量,好用、简单、靠谱。
libuv实现了一个线程池,该线程池在用户提交了第一个任务的时候初始化,而不是系统启动的时候就初始化。入口代码如下。
信号量的运用环境与互斥锁一样,但是信号量比互斥锁增加灵活,互斥锁只有两个状态(开锁和解锁),而信号量本质上是一个计数器,它内部有一个变量计数信号值,可以保护一个资源可以同时被1个或者2个或者3个线程同时使用,如果信号量的值只是设置1(状态只有0和1),那么和互斥锁就是一样的功能。
与OpenMP相比,Pthreads的使用相对要复杂一些,需要我们显式的创建、管理、销毁线程,但也正因为如此,我们对于线程有更强的控制,可以更加灵活的使用线程。这里主要记录一下Pthreads的基本使用方法,如果不是十分复杂的使用环境,这些知识应该可以了。本文大部分内容都是参考自这里,有兴趣的可以看一下原文。
sem: 0 -> 1 -> 0 若临界资源只有1个,则sem设为1,当要使用临界资源时,sem由1变为0,其他人在想申请,则申请不到挂起排队,等待释放临界资源时 sem由0变为1 ,才可以再申请临界资源 这种信号量称为 二元信号量 ,等同于互斥锁
1 条件变量 条件变量是一种同步机制,允许线程挂起,直到共享数据上的某些条件得到满足。 1.1 相关函数 #include <pthread.h> pthread_cond_t cond = PTHREAD_COND_INITIALIZER; int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t*cond_attr); int pthread_cond_signal(pthread_cond_t *cond); int
领取专属 10元无门槛券
手把手带您无忧上云