从同步互斥机制到智能指针使用成本

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/CppExplore/article/details/5846450

作者:CppExplore  http://www.cppblog.com/CppExplore/http://blog.csdn.net/cppexplore同步发布

一 semaphore机制
古老的信号量机制,80年代初,从System V体系中产生,称为System v信号量。90年代,Posix标准确立,其中的信号量称为posix信号量。当前linux系统支持这两种信号量(man semget/ man sem_post)。System v信号量为有名信号量,可以用于多进程间的互斥同步。posix信号量分有名和无名两种,当前linux只支持无名信号量,可以用于多线程之间的互斥同步,通过共享内存也可以用于多进程之间。
信号量属于内核级别的互斥同步机制,相关调用总是陷入内核态,属于功能强大/重量级的实现。

二 spinlock机制
多核SMP的cpu流行后,出现的新的互斥同步机制。spinlock实现原理为用户态循环尝试机制,不陷入内核态进行线程切换。spinlock的原子性通过CAS(CompareAndSwap)原语实现。使用spinlock时,应该保证保护区间内代码执行迅速,不应该存在io等耗时操作。

多核系统下,针对可快速执行的保护区使用用户态循环尝试机制,可以保证执行线程不需要引起上下文切换即可快速执行通过,这种机制也被形象的称为lock-free机制。我们可以这样理解:lock-free机制即为循环尝试,spinlock是它的具体实现。

 

spinlock的实现有多种,常见的有pthread_spin_lock,该spinlock无限制循环尝试,在多核环境下并且保护区代码执行迅速时,执行线程很快可以拿到锁,但当单核环境或者保护区代码执行慢速时,则会耗尽该线程拥有的时间片之后交出cpu,造成cpu的浪费。另一常见的spinlock是boost智能指针中的实现,进行3次无间断的循环CAS原语判断,之后若再次失败,则调用sleep族函数,交出cpu执行权,待再次分配到cpu时间片后继续进行CAS原语判断操作。


三 mutex机制
mutex属于os之上的再次封装实现。在linux2.6内核上,线程库为nptl,其中的mutex基于futex机制实现,它的实现原理,简单说就是spinlock+semaphore,首先使用spinlock尝试,可以拿到锁则直接向下执行,拿不到锁则执行semaphore机制,陷入内核态,进入线程切换。
在多核环境下,当mutex保护的代码段内无io操作,执行很快时,大多数情况下通过spinlock都可拿到锁,不需要陷入内核态。


四 为智能指针正名(boost)
智能指针的引用计数仅仅为一个整型变量的增减,属于执行迅速的典型,使用spinlock机制保护,最新boost版本中仅仅是spinlock,而非mutex。从性能角度说,使用智能指针的现象是cpu略微上升(循环尝试导致),而并发量/单个请求的响应时间并无明显影响。proactor机制实现的网络层,智能指针基本无法绕过,刻意避免带来的只能是丑陋的代码和维护量的上升。但线程之间尽量避免传递指针(智能指针),通过传递id等代替。
智能指针有它使用的场景,不能滥用,也不能刻意避免。

展开阅读全文

java 同步互斥

09-25

import java.util.Date;rnrnrnpublic class chap8_5 rnrn static sale ssss=new sale(6,0,0);rn static Date date=new Date();rn public static void main(String s[])rn rn int n[]=10,10,5,10,5,5; rn Thread t[]=new Thread[10];rn rn for(int i=0;i<6;i++)rn rn t[i]=new Thread(new custom(i+1,n[i],ssss));rn t[i].start();rn rn loop:rn while(true)rn rn for(int m=0;m<6;m++)rn rn if( t[m].isAlive()) continue loop;rn rn break;rn rn // (Long)(date.getTime())rn rn System.out.println(Integer .toString(date.getHours())+":"+Integer .toString(date.getMinutes())+":"+Integer .toString(date.getSeconds())+" 购票结束");rn rnrnclass salernrn int memontoes,five,ten;rn Date date=new Date();rn sale(int m,int f,int t)rn rn memontoes=m;rn five=f;rn ten=t;rn rn synchronized String salef(int num,int money)rn rn String s=null;rn int seconds = date.getSeconds();rn if(memontoes<=0)rn rn s="没有票了,对不起。 ";rn return s;rn rn if(money==5)rn rn //System.out.println("*******");rn memontoes--;rn five++;rn // System.out.println("-----");rn s="给您票,钱正好。五元:"+five+" 十元:"+ten;rn //notify();rn rn rn else if(money==10)rn rn if(five<1)rn //while()rn rn System.out.println("我是第"+num+"个顾客,售货员说:没有零钱,请等待。。。。");rn trywait();catch(InterruptedException e)rn rn five--;rn ten++;rn memontoes--;rn rn s="给您票,你给我10元,找您5元。五元:"+five+" 十元:"+ten;rn rn if(five>0)rn //System.out.println(num+" notify ");rn rn notify(); rn return s;rn rnrnclass custom implements Runnablernrn sale sa;rn int num,money;rn custom(int n,int m,sale s)rn rn num=n;rn money=m;rn sa=s;rn rn public void goumai()rn rn System.out.println("我是第"+num+"个顾客,售货员说:"+sa.salef(num, money)); rn rn rn public void run() rn rn // TODO Auto-generated method stubrn tryrn rn Thread.sleep(1000);rn rn catch(InterruptedException e)rn rn rn rn goumai();rn rn rn rnrn输出结果:rn我是第1个顾客,售货员说:没有零钱,请等待。。。。rn我是第2个顾客,售货员说:没有零钱,请等待。。。。rn我是第4个顾客,售货员说:没有零钱,请等待。。。。rn我是第6个顾客,售货员说:给您票,钱正好。五元:1 十元:0rn我是第1个顾客,售货员说:给您票,你给我10元,找您5元。五元:0 十元:1rn我是第2个顾客,售货员说:给您票,你给我10元,找您5元。五元:0 十元:2rn我是第3个顾客,售货员说:给您票,钱正好。五元:1 十元:1rn我是第4个顾客,售货员说:给您票,你给我10元,找您5元。五元:0 十元:3rn我是第5个顾客,售货员说:给您票,钱正好。五元:1 十元:2rn21:45:16 购票结束rn怎么结果和我越想的不一样呀,哪里出了问题?rn我这个题目,顾客购买车票,车票每张5元,顾客使用的货币面额为5元和10元。顾客购买车票时,如果没有5元的,顾客只能等待。rn不知道说明白了吗? 论坛

信号量和同步互斥

09-23

进程的互斥与P、V操作 rnrn一、临界资源 rnrn什么是临界资源: rn任何时候只允许一个进程使用的资源为临界资源。 rnrn什么是临界区: rn访问临界资源的代码段为临界区。 rnrn例如: rn代码段1 rna = count; rna--; rncount = a; rn代码段2 rnb = count; rnb++; rncount = b; rn为临界区,count为临界资源。 rnrn对临界资源的访问必须满足以下条件: rn一次只能有一个进程进入,其他进程等待。 rn进入者必须在有限时间内退出。 rn等待者应该有机会进入。 rnrn二、信号量 rnrn信号量的结构模型:(S,Q) rn在2.4.x内核中,信号量数据结构定义为(include/asm/semaphore.h): rnstruct semaphore rnatomic_t count; rnint sleepers; rnwait_queue_head_t wait; rn#if WAITQUEUE_DEBUG rnlong __magic; rn#endif rn; rnrn信号量操作: rn1) 初始化 rn2) P操作 rna) S-- rnb) if(S < 0) 在Q队列中睡眠 rnelse 进入 rn3) V操作 rna) S++ rnb) if(S <= 0) 唤醒一睡眠进程(等待队列中有进程在睡眠) rnelse 继续 rnP操作可以理解为申请资源,V操作可以理解为释放资源,在2.4.x内核semaphore结构中,count是资源计数,为正数或0时表示可用资源数,-1表示没有空闲资源且有等待进程,至于等待进程 rnrn的数量并不关心,实现中依赖了sleepers,比较复杂,但好处是是up操作非常简单,只需汇编原子地将count加1,如果小于等于0表示有进程等待,调用__up_wakeup() --> __up(),否则返回 rnrn。linux中的实现可以到arch/i386/kernel/semaphore.c中看到(2.4.x kernel),__down()和__up()。 rnrn信号量实现: rn用ITRON的一个简单实现做为例子。我粗略地看了一下代码,关于信号量(semaphore)的P/V操作,有计数器控制和等待队列。 rnSEMCB(semaphore control block)结构中有id,队列,计数器等成员。 rnrnOS_AcquireSemaphoreTimeOut() rn ... rnlock() rnsemcb->semcnt-- // counter -- rnif(semcb->semcnt < 0) rn rnmake_wait() // make current task in wait state rnqueue_insert_tpri() // insert by priority to the semaphore's rn//wait queue,which is a circular queue rn rnunlock() rn... rn rnrnOS_FreeSemaphore() rn ... rnlock() rnif(semcp->semcnt < 0) rn rnwait_release_ok() rn| rn-> wait_release() // release a task from wait state rn rnsemcb->semcnt++ // counter ++ rnunlock() rn... rn rn注:V操作中S先加1再判断是否小于等于0和先判断是否小于0再加1是等价的。 rn注:在这个实现中,semcnt值不仅正负分别表示资源空闲或有进程等待,其绝对值也是有意义的,为负时值代表等待的进程数,为正或零时值代表空闲的资源数。 rnrn三、同步互斥模型 rnrnS=1 rnA rnP(S) rn临界区 rnV(S) rnrnB rnP(S) rn临界区 rnV(S) rnrn生产者消费者问题 rn(生产者计算生成数,消费者打印之。) rnrn1个生产者1个消费者,缓冲buf为1 rn2个信号量实现模型:put = 1 get = 0 rnA rn计算x rnP(put) rnbuf = x rnV(get) rnB rnP(get) rny = buf rnV(put) rn打印y rnrn1个生产者2个消费者,缓冲buf为1(2个消费者一个打印奇数一个打印偶数) rn3个信号量实现模型:put = 1 getj = 0 geto = 0 rnA rn计算x rnP(put) rnbuf = x rnif(x为奇) rnV(getj) rnelse rnV(geto) rnB rnP(getj) rny = buf rnV(put) rn打印y rnC rnP(geto) rny = buf rnV(put) rn打印y rn这个模型比较直观,但是用了3个信号量。在计算出x后判断其奇偶再"V"不同的信号量。 rn如果只使用2个信号量如何实现,生产者计算出x后直接"V"一个信号量,2个消费者取出后判断是否打印还是不于理会。 rn2个信号量实现模型:put = 1 get = 0 rnA rn计算x rnP(put) rnbuf = x rnV(get) rnB rnP(get) rny = buf rnif(y为奇) rn rnV(put) rn打印y rn rnelse rnV(get) rnC rnP(get) rny = buf rnif(y为偶) rn rnV(put) rn打印y rn rnelse rnV(get) rnrn1个生产者1个消费者,缓冲buf为m rn2个信号量实现模型:put = m get = 0 rnA rn计算x rnP(put) rnbuf[t] = x rnt = (++t) % m rnV(get) rnB rnP(get) rny = buf[k] rnk = (++k) % m rnV(put) rn打印y rnrnN个生产者M个消费者,缓冲buf为m rn4个信号量实现模型:put = m get = 0 T = 1 K = 1 rnA rn计算x rnP(put) rnP(T) rnbuf[t] = x rnt = (++t) % m rnV(T) rnV(get) rnB rnP(get) rnP(K) rny = buf[k] rnk = (++k) % m rnV(K) rnV(put) rn打印y rnrn四、删除一个信号量 rnrn删除一个信号量,系统应该释放一些资源。如果无进程在等待此信号量,处理比较简单。 rn如果有进程在等待此信号量,如何处理这些进程,有人说kill,但我看到的一个实现是唤醒。 rn除进程之外,这个等待队列也需要释放,否则会造成内存泄露。 rn不仅是删除一个信号量,在系统中当删除某个资源时,会释放等待队列的所有task。 rnrnOS_DelFlag --| rnOS_DelMessageBuffer --|--> call wait_delete(QUEUE *) --> OS_DelSemaphore --| rnrnwhile() rnwait_release();... // release all tasks blocked on specified wait queue rnrn五、由来 rnrn为什么敲打这么个东西。因为有个自主实现的基于ITRON的OS需要测试。在测试其信号量资源时我补充了一个测试用例,即当有进程在等待一个信号量时删除此信号量系统会有如何反应。基于此,先给测试人员做了presentation介绍信号量及同步互斥模型。然后粗略地看了看linux 2.4.x,ITRON和ucLinux三个系统的P/V操作和删除信号量的实现。因工作繁忙没有深入研究,简单整理一下用了两个小时边解bug边偷闲着输入完。然后steedhorse帮忙做了检查,经过了一个小时的讨论,改了一些错误,在此感谢。 rn有的问题我不是很清楚,希望看了后能提出来大家讨论。 论坛

没有更多推荐了,返回首页