C++ 线程的学习---线程同步
因为是学习篇,写下是为了个人的学习与理解。故参考其他文章为多。
- 为什么需要线程同步?
在程序中使用多线程时,一般很少有多个线程能在其生命期内进行完全独立的操作。更多的情况是一些线程进行某些处理操作,而其他的线程必须对其处理结果进行了解。正常情况下对这种处理结果的了解应当在其处理任务完成后进行。
如果不采取适当的措施,其他线程往往会在线程处理任务结束前就去访问处理结果,这就很有可能得到有关处理结果的错误了解。例如,多个线程同时访问同一个全局变量,如果都是读取操作,则不会出现问题。如果一个线程负责改变此变量的值,而其他线程负责同时读取变量内容,则不能保证读取到的数据是经过写线程修改后的。
为了确保读线程读取到的是经过修改的变量,就必须在向变量写入数据时禁止其他线程对其的任何访问,直至赋值过程结束后再解除对其他线程的访问限制。这种保证线程能了解其他线程任务处理结束后的处理结果而采取的保护措施即为线程同步。
- 线程之间通信的两个基本问题是互斥和同步
线程同步是指线程之间所具有的一种制约关系,一个线程的执行依赖另一个线程的消息,当它没有得到另一个线程的消息时应等待,直到消息到达时才被唤醒。
线程互斥是指对于共享的操作系统资源(指的是广义的”资源”,而不是Windows的.res文件,譬如全局变量就是一种共享资源),在各线程访问时的排它性。当有若干个线程都要使用某一共享资源时,任何时刻最多只允许一个线程去使用,其它要使用该资源的线程必须等待,直到占用资源者释放该资源
- 线程互斥是一种特殊的线程同步。
实际上,互斥和同步对应着线程间通信发生的两种情况:
当有多个线程访问共享资源而不使资源被破坏时;
当一个线程需要将某个任务已经完成的情况通知另外一个或多个线程时。
- 从大的方面讲,线程的同步可分用户模式的线程同步和内核对象的线程同步两大类
用户模式中线程的同步方法主要有原子访问和临界区等方法。其特点是同步速度特别快,适合于对线程运行速度有严格要求的场合。
内核对象的线程同步则主要由事件、等待定时器、信号量以及信号灯等内核对象构成。由于这种同步机制使用了内核对象,使用时必须将线程从用户模式切换到内核模式,而这种转换一般要耗费近千个CPU周期,因此同步速度较慢,但在适用性上却要远优于用户模式的线程同步方式。
- 在WIN32中,同步机制主要有:事件(Event); 信号量(semaphore); 互斥量(mutex); 临界区(Critical section)
文档参考: C++线程同步的四种方式(Windows)
代码示例:
Project 1:
#include <iostream> #include <Windows.h> #include <time.h> using namespace std; struct RandomSum { DWORD a; DWORD b; }; int main() { //srand((unsigned int)time(NULL)); cout << "inside process 1" << endl; HANDLE hEvent1 = CreateEvent(NULL, FALSE, TRUE, "MyEvent1"); //TRUE=>初始状态是有信号的 HANDLE hEvent2 = CreateEvent(NULL, FALSE, FALSE, "MyEvent2"); //FALSE=>初始状态是没有信号的 HANDLE create_file_mapping_handle = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 1024 * 1024, "data"); if (create_file_mapping_handle == NULL) { printf("Cannot create file mapping. Error code: %d", GetLastError()); return 0; } unsigned char* pData = (unsigned char*)MapViewOfFile(create_file_mapping_handle, FILE_MAP_ALL_ACCESS, 0, 0, 0); if (pData == NULL) { printf("Cannot get pointer to file mapping. Error code: %d", GetLastError()); CloseHandle(create_file_mapping_handle); return 0; } //create new process PROCESS_INFORMATION process2; STARTUPINFO si; memset(&si, 0, sizeof(si)); si.cb = sizeof(si); if (!CreateProcess("D:\\Project 2.exe", NULL, NULL, NULL, FALSE, 0, NULL, NULL, &si, &process2)) { printf("Cannot create process.\n"); return 0; } CloseHandle(process2.hThread); CloseHandle(process2.hProcess); int counter = 0; while (counter < 100) { DWORD dwWaitResult = WaitForSingleObject( hEvent1, // handle to mutex INFINITE); // no time-out interval if (dwWaitResult == WAIT_OBJECT_0) { cout << "am intrat in sender process si scriu in file mapping" << endl; RandomSum test; DWORD randomNumber = rand() % 50; test.a = randomNumber; test.b = 2 * test.a; memcpy(pData, &test, sizeof(RandomSum)); cout << "in process 1: " << "a = " << test.a << " b= " << test.b << endl; cout << "last cout" << endl; SetEvent(hEvent2); } ++counter; } UnmapViewOfFile(pData); CloseHandle(create_file_mapping_handle); CloseHandle(hEvent1); CloseHandle(hEvent2); //getchar(); return 0; }
Project 2:
#include <iostream> #include <Windows.h> #include <time.h> using namespace std; struct RandomSum { DWORD a; DWORD b; }; int main() { cout << "inside process2" << endl; //srand((unsigned int)time(NULL)); cout << "something new in process2" << endl; HANDLE hEvent1 = OpenEvent(EVENT_MODIFY_STATE | SYNCHRONIZE, FALSE, "MyEvent1"); HANDLE hEvent2 = OpenEvent(EVENT_MODIFY_STATE | SYNCHRONIZE, FALSE, "MyEvent2"); LPCWSTR data = L"data"; HANDLE hData = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, "data"); unsigned char* pData = (unsigned char*)MapViewOfFile(hData, FILE_MAP_ALL_ACCESS, 0, 0, 0); if (pData == NULL) { printf("Cannot get pointer to file mapping. Error code: %d", GetLastError()); CloseHandle(hData); return 0; } int counter = 0; while (counter < 100) { DWORD dwWaitResult = WaitForSingleObject( hEvent2, // handle to mutex INFINITE); // no time-out interval if (dwWaitResult == WAIT_OBJECT_0) { cout << "a inceput procesul 2" << endl; RandomSum test; memcpy(&test, pData, sizeof(RandomSum)); cout << "a is " << test.a << " and b is " << test.b << endl; SetEvent(hEvent1); } ++counter; } UnmapViewOfFile(pData); CloseHandle(hData); CloseHandle(hEvent1); CloseHandle(hEvent2); //getchar(); }
更多样本请参阅: https://stackoverflow.com/a/65084216/11128312