当需要一个计数器来限制可以使用某个线程的数目时,可以使用 “信号量 ”对象。CSemaphore 类的对象保存了对当前访问某一指定资源的线程的计数值,该计数值是当前还可以使用该资源的线程的数目。如果这个计数达到了零,则所有对这个 CSemaphore 类对象所控制的资源的访问尝试都被放入到一个队列中等待,直到超时或计数值不为零时为止。 一个线申请到访问被保护的资源时,计数值减 1;一个线程完成了对被控共享资源的访问时,计数值增 1。这个被 CSemaphore 类对象所控制的资源可以同时接受访问的最大线程
数在该对象的构建函数中指定。
CSemaphore 类的构造函数原型及参数说明如下:
CSemaphore (LONG lInitialCount=1, LONG lMaxCount=1, LPCTSTR pstrName=NULL, LPSECURITY_ATTRIBUTES lpsaAttributes=NULL);
lInitialCount:信号量对象的初始计数值,即可访问线程数目的初始值;
lMaxCount:信号量对象计数值的最大值,该参数决定了同一时刻可访问由信号量保护的资源的线程最大数目;
后两个参数在同一进程中使用一般为 NULL,不作过多讨论;
在用 CSemaphore 类的构造函数创建信号量对象时要同时指出允许的最大资源计数和当前可用资源计数。一般是将当前可用资源计数设置为最大资源计数,每增加一个线程对共享资源的访问,当前可用资源计数就会减 1,只要当前可用资源计数是大 0 的,就可以发出信号量信号。但是当前可用计数减小到 0 时,则说明当前占用资源的线程数已经达到了所允许的最大数目,不能再允许其它线程的进入,此时的信号量信号将无法发出。线程在处理完共享资源后,应在离开的同时通过 ReleaseSemaphore()函数将当前可用资源数加 1。
例程:建立一个基于对话框的工程 CSemaphoreSyn,在对话框 IDD_CSEMAPHORESYN_DIALOG中加入一个按钮和三个编辑框控件,按钮的 ID 为 IDC_START,标题为“同时写‘A’、‘B’、‘C’”;三个编辑框的 ID 分别为 IDC_A、IDC_B 和 IDC_C,属性都选中 Read-only;在 MultiThread10Dlg.h 文件中声明三个线程函数:
#include <afxmt.h> UINT WriteA(LPVOID lpParam); UINT WriteB(LPVOID lpParam); UINT WriteC(LPVOID lpParam);使用 ClassWizard 分别给 IDC_A、IDC_B 和 IDC_C 添加 CEdit 类变量 m_ctrlA、m_ctrlB 和m_ctrlC;在 CCSemaphoreSynDlg.cpp 文件中:
// 资源最大2个,当前可访问线程数2个 CSemaphore semaphoreWrite(2,2); char g_Array[10]; UINT WriteA(LPVOID lpParam) { CEdit* pEdit = (CEdit*)lpParam; pEdit->SetWindowText(""); WaitForSingleObject(semaphoreWrite.m_hObject, INFINITE); CString str; for ( int i=0; i<10; i++) { pEdit->GetWindowText(str); g_Array[i] = 'A'; str = str+g_Array[i]; pEdit->SetWindowText(str); Sleep(500); } ReleaseSemaphore(semaphoreWrite.m_hObject, 1, NULL); return 0; } UINT WriteB(LPVOID lpParam) { CEdit* pEdit = (CEdit*)lpParam; pEdit->SetWindowText(""); WaitForSingleObject(semaphoreWrite.m_hObject, INFINITE); CString str; for ( int i=0; i<10; i++) { pEdit->GetWindowText(str); g_Array[i] = 'B'; str = str+g_Array[i]; pEdit->SetWindowText(str); Sleep(500); } ReleaseSemaphore(semaphoreWrite.m_hObject, 1, NULL); return 0; } UINT WriteC(LPVOID lpParam) { CEdit* pEdit = (CEdit*)lpParam; pEdit->SetWindowText(""); WaitForSingleObject(semaphoreWrite.m_hObject, INFINITE); CString str; for ( int i=0; i<10; i++) { pEdit->GetWindowText(str); g_Array[i] = 'C'; str = str+g_Array[i]; pEdit->SetWindowText(str); Sleep(500); } ReleaseSemaphore(semaphoreWrite.m_hObject, 1, NULL); return 0; }
这三个线程函数不再多说。在信号量对象有信号的状态下,线程执行到 WaitForSingleObject语句处继续执行,同时可用线程数减 1;若线程执行到 WaitForSingleObject 语句时信号量对象无信号,线程就在这里等待,直到信号量对象有信号线程才往下执行。
双击按钮 IDC_START,添加其响应函数:
void CCSemaphoreSynDlg::OnStart() { CWinThread* pWriteA = AfxBeginThread(WriteA, &m_ctrlA, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED); pWriteA->ResumeThread(); CWinThread* pWriteB = AfxBeginThread(WriteB, &m_ctrlB, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED); pWriteB->ResumeThread(); CWinThread* pWriteC = AfxBeginThread(WriteC, &m_ctrlC, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED); pWriteC->ResumeThread(); }工程文件下载: Visual C++信号量线程同步的简单实例工程