一、【临界区】
每个进程中访问临界资源的那段代码称为临界区(Critical Section)(临界 资源是一次仅允许一个进程使用的共享资源)。每次只准许一个进程进入临界区, 进入后不允许其他进程进入。不论是硬件临界资源,还是软件临界资源,多个进程 必须互斥地对它进行访问。
多个进程中涉及到同一个临界资源的临界区称为相关临界区。
【进程进入临界区的调度原则】
如果有若干进程要求进入空闲的临界区,一次仅允许一个进程进入。
任何时候,处于临界区内的进程不可多于一个。如已有进程进入自己的临界区, 则其它所有试图进入临界区的进程必须等待。
进入临界区的进程要在有限时间内退出,以便其它进程能及时进入自己的临界区。
如果进程不能进入自己的临界区,则应让出CPU,避免进程出现“忙等”现象。
代码实现如下:
#include<Windows.h> #include<iostream> using namespace std; CRITICAL_SECTION g_cs; // 临界区全局变量 char g_Str[100]; DWORD WINAPI ThreadFunc1(PVOID pParam) { EnterCriticalSection(&g_cs); //进入临界区 for (int i = 0; i < 100; i++) { g_Str[i] = 'Y'; //为数组赋值 Sleep(10); } LeaveCriticalSection(&g_cs); return 0; } DWORD WINAPI ThreadFunc2(PVOID pParam) { EnterCriticalSection(&g_cs); //进入临界区 for (int i = 0; i < 100; i++) { g_Str[100 - i - 1] = 'I'; //为数组赋值 Sleep(10); } LeaveCriticalSection(&g_cs); return 0; } int main() { //初始化临界区 InitializeCriticalSection(&g_cs); HANDLE hTH1 = CreateThread(NULL, 0, ThreadFunc1, NULL, 0, NULL); HANDLE hTH2 = CreateThread(NULL, 0, ThreadFunc2, NULL, 0, NULL); HANDLE TH[2] = { hTH1, hTH2 }; //等待所有内核对象,TRUE等待所有信号量有效再往下执行,FALSE当有其中一个信号量有效时有向下运行 WaitForMultipleObjects(2, TH, FALSE, INFINITE); //删除临界区 DeleteCriticalSection(&g_cs); printf(g_Str); getchar(); return 0; }
二、【事件】
事件的主要用途是标志事件的发生,并以此协调线程的执行顺序。
用来通知线程有一些事件已发生,从而启动后继续任务的开始。
事件对象也可以通过通知操作方式来保持线程的同步,并且可以实现不同进程中的线程同步操作。
实现代码如下:
//线程间通信--》事件 Event #include<Windows.h> #include<iostream> using namespace std; HANDLE evRead, evFinish; DWORD WINAPI ReadThread(PVOID pParam) { // 当等待仍在挂起状态的时候,句柄被关闭 WaitForSingleObject(evRead, INFINITE); cout << "Reading Event: " << endl; return 0; } DWORD WINAPI WriteThread(PVOID pParam) { cout << "Writing Event: " << endl; SetEvent(evRead); return 0; } int main() { evRead = CreateEvent(NULL, FALSE, FALSE, NULL); evFinish = CreateEvent(NULL, FALSE, FALSE, NULL); CreateThread(NULL, 0, ReadThread, NULL, 0, NULL); Sleep(1000); CreateThread(NULL, 0, WriteThread, NULL, 0, NULL); WaitForSingleObject(evFinish, INFINITE); //等待信号量 cout << "The Program is End,OK\n\n"; return 0; }
三、【互斥量】
互斥量又称互斥锁。互斥量是一个可以处于两态之一的变量:解锁和加锁。
如果不需要信号量的计数能力,有时可以使用信号量的一个简化版本,称为互斥量
(mutex)。
互斥量仅仅适用于管理共享资源或一小段代码。
由于互斥量在实现时既容易又有效,这使得互斥量在实现用户空间线程包时非常有 用。
为协调共同对一个共享资源的单独访问而设计的。
实现代码如下:
#include<Windows.h> #include<iostream> #include<fstream> #include<atltime.h> //CTIME 时间类头文件 using namespace std; fstream fstrobj; DWORD WINAPI ThreadFunc1(PVOID pParam) { HANDLE *phMutex = (HANDLE*)pParam; for (int i = 1; i <= 1000000; i++) { WaitForSingleObject(*phMutex, INFINITE); fstrobj << "ThreadFunc1:" << i << endl; ReleaseMutex(*phMutex); } return 0; } DWORD WINAPI ThreadFunc2(PVOID pParam) { HANDLE *phMutex = (HANDLE*)pParam; for (int i = 1; i <= 1000000; i++) { WaitForSingleObject(*phMutex, INFINITE); fstrobj << "ThreadFunc2:" << i << endl; ReleaseMutex(*phMutex); } return 0; } int main() { //获取初始当前时间 CTime timebg = CTime::GetCurrentTime(); int nHour = timebg.GetHour(); int nMin = timebg.GetMinute(); int nSec = timebg.GetSecond(); fstrobj.open("MutexDataFile.txt", ios::out); if (!fstrobj.is_open()) { cout << "文件打开失败!" << endl; } HANDLE hMutex = CreateMutex(NULL, FALSE, L"PrintMutexDataInfo IS:"); HANDLE TH1 = CreateThread(NULL, 0, ThreadFunc1, &hMutex, 0, NULL); HANDLE TH2 = CreateThread(NULL, 0, ThreadFunc2, &hMutex, 0, NULL); HANDLE hTH[2] = { TH1, TH2 }; WaitForMultipleObjects(2, hTH, TRUE, INFINITE); // 等待互斥量对象 CloseHandle(hMutex); fstrobj.close(); //获取结束当前时间 CTime timeed = CTime::GetCurrentTime(); int nHour2 = timeed.GetHour(); int nMin2 = timeed.GetMinute(); int nSec2 = timeed.GetSecond(); //时间差变量 CTimeSpan timeSpan; timeSpan = timeed - timebg; // 统计出执行200万次需要多长时间 cout << "共用时:" << timeSpan.GetHours() << "小时" << timeSpan.GetMinutes() << "分钟" << timeSpan.GetSeconds() << "秒" << endl; return 0; }
四、【信号量(Semaphores)】
(主要是实现同步,可以跨进程) 信号量是一个内核对象,可用来管理大量有限的系统资源
一个使用计数
32位整数,最大资源数量
32位整数,当前资源数量
信号量使用规则:
当前资源数量大于0,则等待信号量的线程获得资源继续运行,当前资源数量 减1
当前资源数量等于0,则等待信号量的线程继续等待,直到有线程释放信号量, 使当前资源数量大于0
创建信号量
HANDLE CreateSemaphore( PSECURITY_ATTRIBUTES psa, LONG lInitialCount, // initial count LONG lMaximumCount, PCTSTR pszName );
为现有的一个已命名信号机对象创建一个新句柄。
HANDLE OpenSemaphore( DWORD fdwAccess, BOOL bInheritHandle, // inheritance option PCTSTR pszName // object name );
SEMAPHORE_ALL_ACCESS 要求对信号量的完全访问;
SEMAPHORE_MODIFY_STATE 允许使用ReleaseSemaphore函数;
SYNCHRONIZE 允许使用信号量同步。
释放信号量
ReleaseSemaphore( HANDLE hSem, LONG lReleaseCount, PLONG plPreviousCount );
等待互斥量
DWORD WaitForSingleObject( HANDLE hHandle, DWORD dwMilliseconds );
实现代码如下:
#include <Windows.h> #include <stdio.h> DWORD WINAPI ThreadFunc1(LPVOID lParam); DWORD WINAPI ThreadFunc2(LPVOID lParam); DWORD WINAPI ThreadFunc3(LPVOID lParam); DWORD WINAPI ThreadFunc4(LPVOID lParam); DWORD WINAPI ThreadFunc5(LPVOID lParam); DWORD WINAPI ThreadFunc6(LPVOID lParam); DWORD WINAPI ThreadFunc7(LPVOID lParam); HANDLE hSM1; HANDLE hSM2; HANDLE hSM3; HANDLE hSM4; HANDLE hSM5; HANDLE hSM6; HANDLE hSM7; HANDLE hTH1; HANDLE hTH2; HANDLE hTH3; HANDLE hTH4; HANDLE hTH5; HANDLE hTH6; HANDLE hTH7; int main() { // 创建6个信号量 hSM1 = CreateSemaphore(NULL, 1, 1, "V"); hSM2 = CreateSemaphore(NULL, 0, 1, "i"); hSM3 = CreateSemaphore(NULL, 0, 1, "c"); hSM4 = CreateSemaphore(NULL, 0, 1, "o"); hSM5 = CreateSemaphore(NULL, 0, 1, "N"); hSM6 = CreateSemaphore(NULL, 0, 1, "B"); hSM7 = CreateSemaphore(NULL, 0, 1, " "); // 创建6个线程 hTH1 = CreateThread(NULL, 0, ThreadFunc1, NULL, 0, NULL); hTH2 = CreateThread(NULL, 0, ThreadFunc2, NULL, 0, NULL); hTH3 = CreateThread(NULL, 0, ThreadFunc3, NULL, 0, NULL); hTH4 = CreateThread(NULL, 0, ThreadFunc4, NULL, 0, NULL); hTH5 = CreateThread(NULL, 0, ThreadFunc5, NULL, 0, NULL); hTH6 = CreateThread(NULL, 0, ThreadFunc6, NULL, 0, NULL); hTH7 = CreateThread(NULL, 0, ThreadFunc7, NULL, 0, NULL); // 等待6个线程都执行完毕后 WaitForSingleObject(hTH1, INFINITE); WaitForSingleObject(hTH2, INFINITE); WaitForSingleObject(hTH3, INFINITE); WaitForSingleObject(hTH4, INFINITE); WaitForSingleObject(hTH5, INFINITE); WaitForSingleObject(hTH6, INFINITE); WaitForSingleObject(hTH7, INFINITE); // 关闭句柄 CloseHandle(hTH1); CloseHandle(hTH2); CloseHandle(hTH3); CloseHandle(hTH4); CloseHandle(hTH5); CloseHandle(hTH6); CloseHandle(hTH7); // 关闭信号量 CloseHandle(hSM1); CloseHandle(hSM2); CloseHandle(hSM3); CloseHandle(hSM4); CloseHandle(hSM5); CloseHandle(hSM6); CloseHandle(hSM7); printf("\n\n\n"); return 0; } DWORD WINAPI ThreadFunc1(LPVOID lParam) { for (int i = 0; i < 10; i++) { DWORD dwWait = WaitForSingleObject(hSM1, INFINITE); printf("V"); ReleaseSemaphore(hSM2, 1, NULL); } return 0; } DWORD WINAPI ThreadFunc2(LPVOID lParam) { for (int i = 0; i < 10; i++) { DWORD dwWait = WaitForSingleObject(hSM2, INFINITE); printf("i"); ReleaseSemaphore(hSM3, 1, NULL); } return 0; } DWORD WINAPI ThreadFunc3(LPVOID lParam) { for (int i = 0; i < 10; i++) { DWORD dwWait = WaitForSingleObject(hSM3, INFINITE); printf("c"); ReleaseSemaphore(hSM4, 1, NULL); } return 0; } DWORD WINAPI ThreadFunc4(LPVOID lParam) { for (int i = 0; i < 10; i++) { DWORD dwWait = WaitForSingleObject(hSM4, INFINITE); printf("o"); ReleaseSemaphore(hSM5, 1, NULL); } return 0; } DWORD WINAPI ThreadFunc5(LPVOID lParam) { for (int i = 0; i < 10; i++) { DWORD dwWait = WaitForSingleObject(hSM5, INFINITE); printf("N"); ReleaseSemaphore(hSM6, 1, NULL); } return 0; } DWORD WINAPI ThreadFunc6(LPVOID lParam) { for (int i = 0; i < 10; i++) { DWORD dwWait = WaitForSingleObject(hSM6, INFINITE); printf("B"); ReleaseSemaphore(hSM7, 1, NULL); } return 0; } DWORD WINAPI ThreadFunc7(LPVOID lParam) { for (int i = 0; i < 10; i++) { DWORD dwWait = WaitForSingleObject(hSM7, INFINITE); printf(" "); ReleaseSemaphore(hSM1, 1, NULL); } return 0; }
小结
临界区:主要通过对多线程串行化来访问公共资源或一段代码,速度快,适合控制数据访问场合;
互斥量:为协议共同对一个共享资源数据的单独访问而设计的;
信号量:为控制一个具有有限数量用户资源而设计;
事件:用来通知线程有一些事件即将发生,从而启动后继任何的开始。