📄 4 种进程或线程同步互斥的控制方法 -- asp酷技术网.htm
字号:
<BR> EnterCriticalSection() 进入临界区
<BR> LeaveCriticalSection() 离开临界区
<BR> EnterCriticalSection()语句执行后代码将进入临界区以后无论发生什么,必须确保与之匹配的LeaveCriticalSection()都能够被执行到。否则临界区保护的共享资源将永远不会被释放。虽然临界区同步速度很快,但却只能用来同步本进程内的线程,而不可用来同步多个进程中的线程。
<BR> MFC提供了很多功能完备的类,我用MFC实现了临界区。MFC为临界区提供有一个CCriticalSection类,使用该类进行线程同步处理是非常简单的。只需在线程函数中用CCriticalSection类成员函数Lock()和UnLock()标定出被保护代码片段即可。Lock()后代码用到的资源自动被视为临界区内的资源被保护。UnLock后别的线程才能访问这些资源。
<BR> <BR> //CriticalSection
<BR> CCriticalSection global_CriticalSection;
<BR> <BR> // 共享资源 <BR> char
global_Array[256]; <BR> <BR> //初始化共享资源
<BR> void InitializeArray() <BR> {
<BR> for(int i = 0;i<256;i++) <BR> {
<BR> global_Array[i]=I; <BR> }
<BR> } <BR> <BR> //写线程
<BR> UINT Global_ThreadWrite(LPVOID pParam)
<BR> { <BR> CEdit *ptr=(CEdit *)pParam;
<BR> ptr->SetWindowText(""); <BR> //进入临界区
<BR> global_CriticalSection.Lock(); <BR>
for(int i = 0;i<256;i++) <BR> { <BR>
global_Array[i]=W; <BR>
ptr->SetWindowText(global_Array); <BR> Sleep(10);
<BR> } <BR> //离开临界区 <BR>
global_CriticalSection.Unlock(); <BR> return 0;
<BR> } <BR> <BR> //删除线程
<BR> UINT Global_ThreadDelete(LPVOID pParam)
<BR> { <BR> CEdit *ptr=(CEdit *)pParam;
<BR> ptr->SetWindowText(""); <BR> //进入临界区
<BR> global_CriticalSection.Lock(); <BR>
for(int i = 0;i<256;i++) <BR> { <BR>
global_Array[i]=D; <BR>
ptr->SetWindowText(global_Array); <BR> Sleep(10);
<BR> } <BR> //离开临界区 <BR>
global_CriticalSection.Unlock(); <BR> return 0;
<BR> } <BR> <BR> //创建线程并启动线程
<BR> void CCriticalSectionsDlg::OnBnClickedButtonLock()
<BR> { <BR> //Start the first Thread
<BR> CWinThread *ptrWrite =
AfxBeginThread(Global_ThreadWrite, <BR> &m_Write,
<BR> THREAD_PRIORITY_NORMAL, <BR> 0,
<BR> CREATE_SUSPENDED); <BR>
ptrWrite->ResumeThread(); <BR> <BR>
//Start the second Thread <BR> CWinThread *ptrDelete =
AfxBeginThread(Global_ThreadDelete, <BR> &m_Delete,
<BR> THREAD_PRIORITY_NORMAL, <BR> 0,
<BR> CREATE_SUSPENDED); <BR>
ptrDelete->ResumeThread(); <BR> } <BR>
<BR> 在测试程序中,Lock
UnLock两个按钮分别实现,在有临界区保护共享资源的执行状态,和没有临界区保护共享资源的执行状态。
<BR> 程序运行结果 <BR> <IMG
src="4 种进程或线程同步互斥的控制方法 -- ASP酷技术网.files/14_234611_85wacs.jpg">
<BR> 互斥量(Mutex) <BR>
<BR> 互斥量跟临界区很相似,只有拥有互斥对象的线程才具有访问资源的权限,由于互斥对象只有一个,因此就决定了任何情况下此共享资源都不会同时被多个线程所访问。当前占据资源的线程在任务处理完后应将拥有的互斥对象交出,以便其他线程在获得后得以访问资源。互斥量比临界区复杂。因为使用互斥不仅仅能够在同一应用程序不同线程中实现资源的安全共享,而且可以在不同应用程序的线程之间实现对资源的安全共享。
<BR> <BR> 互斥量包含的几个操作原语:
<BR> CreateMutex() 创建一个互斥量
<BR> OpenMutex() 打开一个互斥量
<BR> ReleaseMutex() 释放互斥量
<BR> WaitForMultipleObjects() 等待互斥量对象 <BR>
<BR> 同样MFC为互斥量提供有一个CMutex类。使用CMutex类实现互斥量操作非常简单,但是要特别注意对CMutex的构造函数的调用
<BR> CMutex( BOOL bInitiallyOwn = FALSE, LPCTSTR
lpszName = NULL, LPSECURITY_ATTRIBUTES lpsaAttribute = NULL)
<BR> 不用的参数不能乱填,乱填会出现一些意想不到的运行结果。 <BR>
<BR> //创建互斥量 <BR> CMutex global_Mutex(0,0,0);
<BR> <BR> // 共享资源 <BR> char
global_Array[256]; <BR> <BR> void
InitializeArray() <BR> { <BR> for(int i =
0;i<256;i++) <BR> { <BR>
global_Array[i]=I; <BR> } <BR> }
<BR> UINT Global_ThreadWrite(LPVOID pParam)
<BR> { <BR> CEdit *ptr=(CEdit *)pParam;
<BR> ptr->SetWindowText(""); <BR>
global_Mutex.Lock(); <BR> for(int i = 0;i<256;i++)
<BR> { <BR> global_Array[i]=W;
<BR> ptr->SetWindowText(global_Array);
<BR> Sleep(10); <BR> } <BR>
global_Mutex.Unlock(); <BR> return 0; <BR> }
<BR> <BR> UINT Global_ThreadDelete(LPVOID
pParam) <BR> { <BR> CEdit *ptr=(CEdit
*)pParam; <BR> ptr->SetWindowText("");
<BR> global_Mutex.Lock(); <BR> for(int i =
0;i<256;i++) <BR> { <BR>
global_Array[i]=D; <BR>
ptr->SetWindowText(global_Array); <BR> Sleep(10);
<BR> } <BR> global_Mutex.Unlock();
<BR> return 0; <BR> }
<BR> 同样在测试程序中,Lock
UnLock两个按钮分别实现,在有互斥量保护共享资源的执行状态,和没有互斥量保护共享资源的执行状态。
<BR> 程序运行结果 <BR> <IMG
src="4 种进程或线程同步互斥的控制方法 -- ASP酷技术网.files/14_234830_b91fmu.jpg">
<BR> <BR> 信号量(Semaphores)
<BR> 信号量对象对线程的同步方式与前面几种方法不同,信号允许多个线程同时使用共享资源,这与操作系统中的PV操作相同。它指出了同时访问共享资源的线程最大数目。它允许多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的最大线程数目。在用CreateSemaphore()创建信号量时即要同时指出允许的最大资源计数和当前可用资源计数。一般是将当前可用资源计数设置为最大资源计数,每增加一个线程对共享资源的访问,当前可用资源计数就会减1,只要当前可用资源计数是大于0的,就可以发出信号量信号。但是当前可用计数减小到0时则说明当前占用资源的线程数已经达到了所允许的最大数目,不能在允许其他线程的进入,此时的信号量信号将无法发出。线程在处理完共享资源后,应在离开的同时通过ReleaseSemaphore()函数将当前可用资源计数加1。在任何时候当前可用资源计数决不可能大于最大资源计数。
<BR> PV操作及信号量的概念都是由荷兰科学家E.W.Dijkstra提出的。信号量S是一个整数,S大于等于零时代表可供并发进程使用的资源实体数,但S小于零时则表示正在等待使用共享资源的进程数。
<BR> P操作 申请资源: <BR> (1)S减1;
<BR> (2)若S减1后仍大于等于零,则进程继续执行;
<BR> (3)若S减1后小于零,则该进程被阻塞后进入与该信号相对应的队列中,然后转入进程调度。
<BR> V操作 释放资源: <BR> (1)S加1;
<BR> (2)若相加结果大于零,则进程继续执行;
<BR> (3)若相加结果小于等于零,则从该信号的等待队列中唤醒一个等待进程,然后再返回原进程继续执行或转入进程调度。
<BR> <BR> 信号量包含的几个操作原语:
<BR> CreateSemaphore() 创建一个信号量
<BR> OpenSemaphore() 打开一个信号量
<BR> ReleaseSemaphore() 释放信号量
<BR> WaitForSingleObject() 等待信号量 <BR>
<BR> //信号量句柄 <BR> HANDLE global_Semephore;
<BR> <BR> // 共享资源 <BR> char
global_Array[256]; <BR> void InitializeArray()
<BR> { <BR> for(int i = 0;i<256;i++)
<BR> { <BR> global_Array[i]=I;
<BR> } <BR> } <BR> //线程1
<BR> UINT Global_ThreadOne(LPVOID pParam)
<BR> { <BR> CEdit *ptr=(CEdit *)pParam;
<BR> ptr->SetWindowText(""); <BR>
//等待对共享资源请求被通过 等于 P操作
<BR> WaitForSingleObject(global_Semephore, INFINITE);
<BR> for(int i = 0;i<256;i++) <BR> {
<BR> global_Array[i]=O; <BR>
ptr->SetWindowText(global_Array); <BR> Sleep(10);
<BR> } <BR> //释放共享资源 等于 V操作 <BR>
ReleaseSemaphore(global_Semephore, 1, NULL); <BR> return
0; <BR> } <BR> <BR> UINT
Global_ThreadTwo(LPVOID pParam) <BR> { <BR>
CEdit *ptr=(CEdit *)pParam; <BR>
ptr->SetWindowText(""); <BR>
WaitForSingleObject(global_Semephore, INFINITE); <BR>
for(int i = 0;i<256;i++) <BR> { <BR>
global_Array[i]=T; <BR>
ptr->SetWindowText(global_Array); <BR> Sleep(10);
<BR> } <BR>
ReleaseSemaphore(global_Semephore, 1, NULL); <BR> return
0; <BR> } <BR> <BR> UINT
Global_ThreadThree(LPVOID pParam) <BR> { <BR>
CEdit *ptr=(CEdit *)pParam; <BR>
ptr->SetWindowText(""); <BR>
WaitForSingleObject(global_Semephore, INFINITE); <BR>
for(int i = 0;i<256;i++) <BR> { <BR>
global_Array[i]=H; <BR>
ptr->SetWindowText(global_Array); <BR> Sleep(10);
<BR> } <BR>
ReleaseSemaphore(global_Semephore, 1, NULL); <BR> return
0; <BR> } <BR> <BR> void
CSemaphoreDlg::OnBnClickedButtonOne() <BR> {
<BR> //设置信号量 1 个资源 1同时只可以有一个线程访问 <BR>
global_Semephore= CreateSemaphore(NULL, 1, 1, NULL);
<BR> this->StartThread(); <BR> // TODO:
Add your control notification handler code here <BR> }
<BR> <BR> void
CSemaphoreDlg::OnBnClickedButtonTwo() <BR> {
<BR> //设置信号量 2 个资源 2 同时只可以有两个线程访问 <BR>
global_Semephore= CreateSemaphore(NULL, 2, 2, NULL);
<BR> this->StartThread(); <BR> // TODO:
Add your control notification handler code here <BR> }
<BR> <BR> void
CSemaphoreDlg::OnBnClickedButtonThree() <BR> {
<BR> //设置信号量 3 个资源 3 同时只可以有三个线程访问 <BR>
global_Semephore= CreateSemaphore(NULL, 3, 3, NULL);
<BR> this->StartThread(); <BR> // TODO:
Add your control notification handler code here <BR> }
<BR> 信号量的使用特点使其更适用于对Socket(套接字)程序中线程的同步。例如,网络上的HTTP服务器要对同一时间内访问同一页面的用户数加以限制,这时可以为每一个用户对服务器的页面请求设置一个线程,而页面则是待保护的共享资源,通过使用信号量对线程的同步作用可以确保在任一时刻无论有多少用户对某一页面进行访问,只有不大于设定的最大用户数目的线程能够进行访问,而其他的访问企图则被挂起,只有在有用户退出对此页面的访问后才有可能进入。
<BR> 程序运行结果 <BR> <IMG
src="4 种进程或线程同步互斥的控制方法 -- ASP酷技术网.files/14_235300_41s7sp.jpg">
<BR> <BR> 事件(Event) <BR>
<BR> 事件对象也可以通过通知操作的方式来保持线程的同步。并且可以实现不同进程中的线程同步操作。
<BR> 信号量包含的几个操作原语: <BR> CreateEvent()
创建一个信号量 <BR> OpenEvent() 打开一个事件
<BR> SetEvent() 回置事件
<BR> WaitForSingleObject() 等待一个事件
<BR> WaitForMultipleObjects() 等待多个事件
<BR> WaitForMultipleObjects 函数原型:
<BR> WaitForMultipleObjects( <BR> IN
DWORD nCount, // 等待句柄数 <BR> IN CONST HANDLE
*lpHandles, //指向句柄数组 <BR> IN BOOL bWaitAll,
//是否完全等待标志 <BR> IN DWORD dwMilliseconds //等待时间
<BR> )
<BR> 参数nCount指定了要等待的内核对象的数目,存放这些内核对象的数组由lpHandles来指向。fWaitAll对指定的这nCount个内核对象的两种等待方式进行了指定,为TRUE时当所有对象都被通知时函数才会返回,为FALSE则只要其中任何一个得到通知就可以返回。dwMilliseconds在这里的作用与在WaitForSingleObject()中的作用是完全一致的。如果等待超时,函数将返回WAIT_TIMEOUT。
<BR> <BR> //事件数组 <BR> HANDLE
global_Events[2]; <BR> <BR> // 共享资源
<BR> char global_Array[256]; <BR>
<BR> void InitializeArray() <BR> {
<BR> for(int i = 0;i<256;i++) <BR> {
<BR> global_Array[i]=I; <BR> }
<BR> } <BR> <BR> UINT
Global_ThreadOne(LPVOID pParam) <BR> { <BR>
CEdit *ptr=(CEdit *)pParam; <BR>
ptr->SetWindowText(""); <BR> for(int i =
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -