(转)线程同步技术(二)

来源:互联网 发布:淘宝微信投票怎么刷票 编辑:程序博客网 时间:2024/05/16 23:43
  
  互斥内核

  互斥(Mutex)是一用途非常广泛的内核象。能多个线同一共享源的互斥访问界区有些似,只有有互斥象的线程才具有访问资源的限,由于互斥象只有一个,因此就决定了任何情况下此共享源都不会同被多个线程所访问。以便其他线程在得后得以访问资源。与其他几内核象不同,互斥象在操作系有特殊代,并由操作系来管理,操作系甚至行一些其他内核象所不能行的非常操作。
  以互斥内核象来保持线程同可能用到的函数主要有CreateMutex()、OpenMutex()、ReleaseMutex()、WaitForSingleObject()和WaitForMultipleObjects()等。在使用互斥象前,首先要通CreateMutex()或OpenMutex()建或打一个互斥象。CreateMutex()函数原型
HANDLE CreateMutex(
 LPSECURITY_ATTRIBUTES lpMutexAttributes, // 安全属性指
 BOOL bInitialOwner, // 初始有者
 LPCTSTR lpName // 互斥象名
);
 参数bInitialOwner主要用来控制互斥象的初始状。一般多将其FALSE,以表明互斥象在并没有任何线程所占有。如果在建互斥指定了象名,那可以在本程其他地方或是在其他程通OpenMutex()函数得到此互斥象的句柄。OpenMutex()函数原型
HANDLE OpenMutex(
 DWORD dwDesiredAccess, // 访问标
 BOOL bInheritHandle, //
 LPCTSTR lpName // 互斥象名
);

  当目前对资源具有访问权线程不再需要访问源而要离开时,必ReleaseMutex()函数来放其有的互斥象,其函数原型
BOOL ReleaseMutex(HANDLE hMutex);
  其唯一的参数hMutex放的互斥象句柄。至于WaitForSingleObject()和WaitForMultipleObjects()等待函数在互斥象保持线程同中所起的作用与在其他内核象中的作用是基本一致的,也是等待互斥内核象的通知。但是里需要特指出的是:在互斥象通知引起用等待函数返回,等待函数的返回不再是通常的WAIT_OBJECT_0WaitForSingleObject()函数)或是在WAIT_OBJECT_0WAIT_OBJECT_0+nCount-1的一个WaitForMultipleObjects()函数),而是将返回一个WAIT_ABANDONED_0WaitForSingleObject()函数)或是在WAIT_ABANDONED_0WAIT_ABANDONED_0+nCount-1的一个WaitForMultipleObjects()函数)。以此来表明线程正在等待的互斥象由另外一个线程所有,而此线程却在使用完共享源前就已经终止。除此之外,使用互斥象的方法在等待线程的可度性上同使用其他几内核象的方法也有所不同,其他内核象在没有得到通,受用等待函数的作用,线程将会挂起,同失去可度性,而使用互斥的方法却可以在等待的同仍具有可度性,也正是互斥象所能完成的非常操作之一
  在写程序,互斥象多用在那些多个线程所访问的内的保上,可以确保任何线程在理此内存块时有可靠的独占访问权。下面出的示例代即通互斥内核hMutex共享内存快g_cArray[]线程的独占访问下面实现
// 互斥
HANDLE hMutex = NULL;
char g_cArray[10];
UINT ThreadProc18(LPVOID pParam)
{
 // 等待互斥象通知
 WaitForSingleObject(hMutex, INFINITE);
 // 共享行写入操作
 for (int i = 0; i < 10; i++)
 {
  g_cArray[i] = 'a';
  Sleep(1);
 }
 // 放互斥
 ReleaseMutex(hMutex);
 return 0;
}
UINT ThreadProc19(LPVOID pParam)
{
 // 等待互斥象通知
 WaitForSingleObject(hMutex, INFINITE);
 // 共享行写入操作
 for (int i = 0; i < 10; i++)
 {
  g_cArray[10 - i - 1] = 'b';
  Sleep(1);
 }
 // 放互斥
 ReleaseMutex(hMutex);
 return 0;
}
……
void CSample08View::OnMutex()
{
 // 建互斥
 hMutex = CreateMutex(NULL, FALSE, NULL);
 // 动线
 AfxBeginThread(ThreadProc18, NULL);
 AfxBeginThread(ThreadProc19, NULL);
 // 等待算完
 Sleep(300);
 //
 CString sResult = CString(g_cArray);
 AfxMessageBox(sResult);
}
  互斥象在MFC中通CMutex类进行表述。使用CMutex的方法非常简单,在构造CMutex类对象的同可以指明待查询的互斥象的名字,在构造函数返回后即可访问此互斥量。CMutex也是只含有构造函数唯一的成函数,当完成互斥象保护资源的访问后,可通过调用从父CSyncObject承的UnLock()函数完成互斥象的放。CMutex构造函数原型
CMutex( BOOL bInitiallyOwn = FALSE, LPCTSTR lpszName = NULL, LPSECURITY_ATTRIBUTES lpsaAttribute = NULL );
  该类的适用范实现原理与API方式建的互斥内核象是完全似的,但要简洁的多,下面出就是前面的示例代码经CMutex改写后的程序实现
// MFC互斥类对
CMutex g_clsMutex(FALSE, NULL);
UINT ThreadProc27(LPVOID pParam)
{
 // 等待互斥象通知
 g_clsMutex.Lock();
 // 共享行写入操作
 for (int i = 0; i < 10; i++)
 {
  g_cArray[i] = 'a';
  Sleep(1);
 }
 // 放互斥
 g_clsMutex.Unlock();
 return 0;
}
UINT ThreadProc28(LPVOID pParam)
{
 // 等待互斥象通知
 g_clsMutex.Lock();
 // 共享行写入操作
 for (int i = 0; i < 10; i++)
 {
  g_cArray[10 - i - 1] = 'b';
  Sleep(1);
 }
 // 放互斥
 g_clsMutex.Unlock();
 return 0;
}
……
void CSample08View::OnMutexMfc()
{
 // 动线
 AfxBeginThread(ThreadProc27, NULL);
 AfxBeginThread(ThreadProc28, NULL);
 // 等待算完
 Sleep(300);
 //
 CString sResult = CString(g_cArray);
 AfxMessageBox(sResult);
}
 
信号量内核
 信号量(Semaphore)内核对线程的同方式与前面几方法不同,它允多个线程在同一访问同一源,但是需要限制在同一访问源的最大线程数目。在用CreateSemaphore()建信号量即要同指出允的最大数和当前可用数。一般是将当前可用最大数,增加一个线共享源的访问,当前可用数就会减1,只要当前可用数是大于0的,就可以出信号量信号。但是当前可用数减小到0时则说明当前占用源的线程数已达到了所允的最大数目,不能在允其他线程的入,此的信号量信号将无法出。线程在理完共享源后,在离的同ReleaseSemaphore()函数将当前可用数加1。在任何候当前可用数决不可能大于最
 使用信号量内核线程同主要会用到CreateSemaphore()、OpenSemaphore()、ReleaseSemaphore()、WaitForSingleObject()和WaitForMultipleObjects()等函数。其中,CreateSemaphore()用来建一个信号量内核象,其函数原型
HANDLE CreateSemaphore(
 LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, // 安全属性指
 LONG lInitialCount, // 初始
 LONG lMaximumCount, // 最大
 LPCTSTR lpName // 象名指
);

  参数lMaximumCount是一个有符号32,定了允的最大数,最大取不能超4294967295lpName参数可以为创建的信号量定一个名字,由于其建的是一个内核象,因此在其他程中可以通过该名字而得到此信号量。OpenSemaphore()函数即可用来根据信号量名打在其他程中建的信号量,函数原型如下:
HANDLE OpenSemaphore(
 DWORD dwDesiredAccess, // 访问标
 BOOL bInheritHandle, //
 LPCTSTR lpName // 信号量名
);
  在线程离开对共享源的,必ReleaseSemaphore()来增加当前可用数。否将会出当前正在理共享源的实际线程数并没有达到要限制的数,而其他线程却因当前可用0而仍无法入的情况。ReleaseSemaphore()的函数原型
BOOL ReleaseSemaphore(
 HANDLE hSemaphore, // 信号量句柄
 LONG lReleaseCount, // 增数量
 LPLONG lpPreviousCount // 先前
);

  函数将lReleaseCount中的添加信号量的当前数,一般将lReleaseCount1,如果需要也可以置其他的WaitForSingleObject()和WaitForMultipleObjects()主要用在试图进入共享源的线程函数入口,主要用来判断信号量的当前可用数是否允线程的入。只有在当前可用大于0,被监视的信号量内核象才会得到通知。

  信号量的使用特点使其更适用于Socket(套接字)程序中线的同。例如,网上的HTTP器要同一时间访问同一面的用数加以限制,这时可以没一个用户对器的置一个线程,而是待保的共享源,通使用信号量对线程的同作用可以确保在任一刻无有多少用户对某一访问,只有不大于定的最大用数目的线程能够进访问,而其他的访问图则被挂起,只有在有用退出面的访问后才有可能入。下面出的示例代即展示了似的
// 信号量象句柄
HANDLE hSemaphore;
UINT ThreadProc15(LPVOID pParam)
{
 // 试图进入信号量
 WaitForSingleObject(hSemaphore, INFINITE);
 // 线程任务处
 AfxMessageBox("线程一正在!");
 // 放信号量
 ReleaseSemaphore(hSemaphore, 1, NULL);
 return 0;
}
UINT ThreadProc16(LPVOID pParam)
{
 // 试图进入信号量
 WaitForSingleObject(hSemaphore, INFINITE);
 // 线程任务处
 AfxMessageBox("线程二正在!");
 // 放信号量
 ReleaseSemaphore(hSemaphore, 1, NULL);
 return 0;
}
UINT ThreadProc17(LPVOID pParam)
{
 // 试图进入信号量
 WaitForSingleObject(hSemaphore, INFINITE);
 // 线程任务处
 AfxMessageBox("线程三正在!");
 // 放信号量
 ReleaseSemaphore(hSemaphore, 1, NULL);
 return 0;
}
……
void CSample08View::OnSemaphore()
{
 // 建信号量
 hSemaphore = CreateSemaphore(NULL, 2, 2, NULL);
 // 线
 AfxBeginThread(ThreadProc15, NULL);
 AfxBeginThread(ThreadProc16, NULL);
 AfxBeginThread(ThreadProc17, NULL);
}
上述代码在开启线程前首先创建了一个初始计数和最大资源计数均为2的信号量对象hSemaphore。即在同一时刻只允许2个线程进入由hSemaphore保护的共享资源。随后开启的三个线程均试图访问此共享资源,在前两个线程试图访问共享资源时,由于hSemaphore的当前可用资源计数分别为2和1,此时的hSemaphore是可以得到通知的,也就是说位于线程入口处的WaitForSingleObject()将立即返回,而在前两个线程进入到保护区域后,hSemaphore的当前资源计数减少到0,hSemaphore将不再得到通知,WaitForSingleObject()将线程挂起。直到此前进入到保护区的线程退出后才能得以进入。图4和图5为上述代脉的运行结果。从实验结果可以看出,信号量始终保持了同一时刻不超过2个线程的进入。
MFC中,通CSemaphore类对信号量作了表述。该类只具有一个构造函数,可以构造一个信号量象,并初始数、最大数、象名和安全属性等行初始化,其原型如下:
CSemaphore( LONG lInitialCount = 1, LONG lMaxCount = 1, LPCTSTR pstrName = NULL, LPSECURITY_ATTRIBUTES lpsaAttributes = NULL );

  在构造了CSemaphore类对象后,任何一个访问受保共享源的线程都必CSemaphore从父CSyncObject类继承得到的Lock()和UnLock()成函数来访问CSemaphore象。与前面介的几MFC保持线程同的方法似,通CSemaphore也可以将前面的线程同码进行改写,使用信号量的线程同方法无是在实现原理是从实现结果上都是完全一致的。下面MFC改写后的信号量线程同
// MFC信号量类对
CSemaphore g_clsSemaphore(2, 2);
UINT ThreadProc24(LPVOID pParam)
{
 // 试图进入信号量
 g_clsSemaphore.Lock();
 // 线程任务处
 AfxMessageBox("线程一正在!");
 // 放信号量
 g_clsSemaphore.Unlock();
 return 0;
}
UINT ThreadProc25(LPVOID pParam)
{
 // 试图进入信号量
 g_clsSemaphore.Lock();
 // 线程任务处
 AfxMessageBox("线程二正在!");
 // 放信号量
 g_clsSemaphore.Unlock();
 return 0;
}
UINT ThreadProc26(LPVOID pParam)
{
 // 试图进入信号量
 g_clsSemaphore.Lock();
 // 线程任务处
 AfxMessageBox("线程三正在!");
 // 放信号量
 g_clsSemaphore.Unlock();
 return 0;
}
……
void CSample08View::OnSemaphoreMfc()
{
 // 线
 AfxBeginThread(ThreadProc24, NULL);
 AfxBeginThread(ThreadProc25, NULL);
 AfxBeginThread(ThreadProc26, NULL);
}
 
原创粉丝点击