进程同步及线程同步的几种机制 |
您所在的位置:网站首页 › 消费型保险推荐机制有哪些 › 进程同步及线程同步的几种机制 |
进程中线程同步的四种常用方式: 1. 互斥量: 采用互斥对象机制,只有拥有互斥对象的线程才有访问公共资源的权限。因为互斥对象只有一个,所以可以保证公共资源不会被多个线程同时访问。 2. 信号量: 它允许同一时刻多个线程来访问同一资源,但是需要控制同一时刻访问此资源的最大线程数量。 3. 事件(信号):通过通知操作的方式来保持多线程同步,还可以方便实现多线程优先级的比较作。 4.临界区:临界区对象和互斥对象非常相似,只是互斥量允许在进程间使用,而临界区只限制与同一进程的各个线程之间使用,但是更节省资源,更有效率。 临界区: 当多个线程访问一个独占性共享资源时,可以使用临界区对象。拥有临界区的线程可以访问被保护起来的资源或代码段,其他线程若想访问,则被挂起,直到拥有临界区的线程放弃临界区为止。 1、 临界区(CCriticalSection) 当多个线程访问一个独占性共享资源时,可以使用临界区对象。拥有临界区的线程可以访问被保护起来的资源或代码段,其他线程若想访问,则被挂起,直到拥有临界区的线程放弃临界区为止。具体应用方式: 1、 定义临界区对象CcriticalSection g_CriticalSection; 2、 在访问共享资源(代码或变量)之前,先获得临界区对象,g_CriticalSection.Lock(); 3、 访问共享资源后,则放弃临界区对象,g_CriticalSection.Unlock();
2、 事件(CEvent) 事件机制,则允许一个线程在处理完一个任务后,主动唤醒另外一个线程执行任务。比如在某些网络应用程序中,一个线程如A负责侦听通信端口,另外一个线程B负责更新用户数据,利用事件机制,则线程A可以通知线程B何时更新用户数据。每个Cevent对象可以有两种状态:有信号状态和无信号状态。Cevent类对象有两种类型:人工事件和自动事件。 自动事件对象,在被至少一个线程释放后自动返回到无信号状态; 人工事件对象,获得信号后,释放可利用线程,但直到调用成员函数ReSet()才将其设置为无信号状态。在创建Cevent对象时,默认创建的是自动事件。 1、1 2 3 4 CEvent(BOOL bInitiallyOwn=FALSE, BOOL bManualReset=FALSE, LPCTSTR lpszName=NULL, LPSECURITY_ATTRIBUTES lpsaAttribute=NULL); bInitiallyOwn:指定事件对象初始化状态,TRUE为有信号,FALSE为无信号;bManualReset:指定要创建的事件是属于人工事件还是自动事件。TRUE为人工事件,FALSE为自动事件;后两个参数一般设为NULL,在此不作过多说明。 2、BOOL CEvent::SetEvent();将Cevent类对象的状态设置为有信号状态。如果事件是人工事件,则Cevent类对象保持为有信号状态,直到调用成员函数ResetEvent()将其重新设为无信号状态时为止。如果为自动事件,则在SetEvent()后将事件设置为有信号状态,由系统自动重置为无信号状态。 3、BOOL CEvent::ResetEvent();将事件的状态设置为无信号状态,并保持该状态直至SetEvent()被调用为止。由于自动事件是由系统自动重置,故自动事件不需要调用该函数。 一般通过调用WaitForSingleObject()函数来监视事件状态。
3、 互斥量(CMutex) 互斥对象和临界区对象非常相似,只是其允许在进程间使用,而临界区只限制与同一进程的各个线程之间使用, 但是更节省资源,更有效率。 4、 信号量(CSemphore) 当需要一个计数器来限制可以使用某共享资源的线程数目时,可以使用“信号量”对象。CSemaphore类对象保存了对当前访问某一个指定资源的线程的计数值,该计数值是当前还可以使用该资源的线程数目。如果这个计数达到了零,则所有对这个CSemaphore类对象所控制的资源的访问尝试都被放入到一个队列中等待,直到超时或计数值不为零为止。 CSemaphore 类的构造函数原型及参数说明如下: 1 2 3 4 5 6 CSemaphore( LONG lInitialCount = 1, LONG lMaxCount = 1, LPCTSTR pstrName = NULL, LPSECURITY_ATTRIBUTES lpsaAttributes = NULL ); lInitialCount:信号量对象的初始计数值,即可访问线程数目的初始值;lMaxCount:信号量对象计数值的最大值,该参数决定了同一时刻可访问由信号量保护的资源的线程最大数目;后两个参数在同一进程中使用一般为NULL,不作过多讨论;一般是将当前可用资源计数设置为最大资源计数,每增加一个线程对共享资源的访问,当前可用资源计数就减1,只要当前可用资源计数大于0,就可以发出信号量信号。如果为0,则放入一个队列中等待。线程在处理完共享资源后,应在离开的同时通过ReleaseSemaphore()函数将当前可用资源数加1。
1 2 3 BOOL ReleaseSemaphore( HANDLE hSemaphore, // hSemaphore:信号量句柄 LONG lReleaseCount, // lReleaseCount:信号量计数值 LPLONG lpPreviousCount // 参数一般为NULL); ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ 进程同步: 多进程的系统中避免不了进程间的相互关系。本讲将介绍进程间的两种主要关系——同步与互斥,然后着重讲解解决进程同步的几种机制。 进程互斥是进程之间发生的一种间接性作用,一般是程序不希望的。通常的情况是两个或两个以上的进程需要同时访问某个共享变量。我们一般将发生能够问共享变量的程序段称为临界区。两个进程不能同时进入临界区,否则就会导致数据的不一致,产生与时间有关的错误。解决互斥问题应该满足互斥和公平两个原则,即任意时刻只能允许一个进程处于同一共享变量的临界区,而且不能让任一进程无限期地等待。互斥问题可以用硬件方法解决,我们不作展开;也可以用软件方法,这将会在本讲详细介绍。 进程同步是进程之间直接的相互作用,是合作进程间有意识的行为,典型的例子是公共汽车上司机与售票员的合作。只有当售票员关门之后司机才能启动车辆,只有司机停车之后售票员才能开车门。司机和售票员的行动需要一定的协调。同样地,两个进程之间有时也有这样的依赖关系,因此我们也要有一定的同步机制保证它们的执行次序。 本讲主要介绍以下四种同步和互斥机制:信号量、管程、会合、分布式系统。 一,信号量 参考自http://blog.csdn.net/leves1989/article/details/3305609 理解PV: PV操作由P操作原语和V操作原语组成(原语是不可中断的过程),对信号量进行操作,具体定义如下: P(S):①将信号量S的值减1,即S=S-1; ②如果S³0,则该进程继续执行;否则该进程置为等待状态,排入等待队列。 V(S):①将信号量S的值加1,即S=S+1; ②如果S>0,则该进程继续执行;否则释放队列中第一个等待信号量的进程。PV操作的意义:我们用信号量及PV操作来实现进程的同步和互斥。PV操作属于进程的低级通信。 什么是信号量?信号量(semaphore)的数据结构为一个值和一个指针,指针指向等待该信号量的下一个进程。信号量的值与相应资源的使用情况有关。当它的值大于0时,表示当前可用资源的数量;当它的值小于0时,其绝对值表示等待使用该资源的进程个数。注意,信号量的值仅能由PV操作来改变。 一般来说,信号量S³0时,S表示可用资源的数量。执行一次P操作意味着请求分配一个单位资源,因此S的值减1;当Svalue--; if(S->value list; block(); }} signal()信号量部分代码如下: signal(semaphore *S) { S->value++; if(S->value list; wakeup(P); }}一、The Bounded-Buffer Problem: full初始化为0,empty初始化为n,mutex为1 do{ wait(full); wait(mutex); ... //remove an item from buffer to nextc ... signal(mutex); signal(empty); ... //consume the item in nextc ...} while(TRUE);二、The Readers-Writers Problem: wrt初始化为1,readcount初始化为0,mutex为1 写者操作: do{ wait(wrt); ... //writing is performed ... signal(wrt);} while(TRUE);
读者操作: do{ wait(mutex);//确保与signal(mutex)之间的操作不会被其他读者打断 readcount++; if(readcount == 1) wait(wrt); signal(mutex); ... //reading is performed ... wait(mutex); readcount--; if(readcount == 0) signal(wrt); signal(mutex);} while(TRUE);
三、The Dining-Philosophers Problem:
所有的chopstick[5]全部初始化为1 do{ wait(chopstick[i]); wait(chopstick[(i+1)%5]); ... //eating ... signal(chopstick[i]); signal(chopstick[(i+1)%5]); ... //thinking ...} while(TRUE);但是这个解决方案的最大问题在于它会出现死锁。所以我认为应该增加一个信号量mutex,并初始化为1: do{ wait(mutex); wait(chopstick[i]); wait(chopstick[(i+1)%5]); signal(mutex); ... //eating ... wait(mutex); signal(chopstick[i]); signal(chopstick[(i+1)%5]); signal(mutex); ... //thinking ...} while(TRUE);这样由于确保了一位哲学家在拿起两只筷子的时间内其他哲学家不可以拿起任何一支筷子,从而破坏了死锁出现需要的四个特征中的Hold And Wait特征,从而避免了死锁的发生。 |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |