Windows
同步
多线程共享资源同步原因
以一个双线程计数为例:
建立一个控制台应用:

线程回调:

创建两个线程

| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 
 | 
 
 #include<Windows.h>
 #include <iostream>
 using namespace std;
 
 int g_nCounter = 0;
 
 DWORD WINAPI ThreadFunc(LPVOID lpParam)
 {
 for (int i = 0; i < 0x10000; i++) {
 ++g_nCounter;
 }
 printf("tid:%08x counter:%08x \r\n", GetCurrentThreadId(), g_nCounter);
 
 return 0;
 }
 
 int main()
 {
 DWORD dwThreadId, dwThrdParam = 1;
 HANDLE hThread[2];
 hThread[0] = CreateThread(
 NULL,
 0,
 ThreadFunc,
 &dwThrdParam,
 0,
 &dwThreadId);
 
 hThread[1] = CreateThread(
 NULL,
 0,
 ThreadFunc,
 &dwThrdParam,
 0,
 &dwThreadId);
 
 
 system("pause");
 cout << "Hello World!\n";
 
 }
 
 
 | 
两个线程运行结果应该为00020000 但是实际上无法达到 由于多线程的同步问题

++g_nCounter:

多线程同步可能发生的情况:
当线程1执行到加1时 线程被切到线程2 线程2执行完 再切回线程1 而线程1在被切走的时候内存为10 接着执行就会变成11(环境保存和恢复) 相当于两次加1 实际上就只有一次

三环同步
原子操作
原子操作(atomic operation)指的是由多步操作组成的一个操作。如果该操作不能原子地执行,则要么执行完所有步骤,要么一步也不执行,不可能只执行所有步骤的一个子集。
例如刚才的++g_nCounter 为三步操作 这三步使用原子操作时 必须全部执行 不能中途切换
windows原子操作API


| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 
 | #include<Windows.h>#include <iostream>
 using namespace std;
 
 LONG g_nCounter = 0;
 
 DWORD WINAPI ThreadFunc(LPVOID lpParam)
 {
 for (int i = 0; i < 0x10000; ++i) {
 InterlockedIncrement( &g_nCounter);
 }
 printf("tid:%08X counter:%08X \r\n", GetCurrentThreadId(), g_nCounter);
 
 return 0;
 }
 
 int main()
 {
 DWORD dwThreadId, dwThrdParam = 1;
 HANDLE hThread[2];
 hThread[0] = CreateThread(
 NULL,
 0,
 ThreadFunc,
 &dwThrdParam,
 0,
 &dwThreadId);
 
 hThread[1] = CreateThread(
 NULL,
 0,
 ThreadFunc,
 &dwThrdParam,
 0,
 &dwThreadId);
 
 
 system("pause");
 cout << "Hello World!\n";
 }
 
 | 

由于两个线程是同时进行的(不是先做完线程1 再做线程2  ,二者切换)所以线程1最后的结果并不是00010000
临界区(关键段)
当遇到更加复杂的情况多个线程同时修改了 vector 或者其它需要保护的共享资源时原本上述的API就不适用了 这时候就得使用临界区(同进程)
进入临界区:

离开临界区:

初始化(一次即可):

| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 
 | 
 
 #include<Windows.h>
 #include <iostream>
 #include<vector>
 using namespace std;
 #pragma pack(1)
 
 vector<int> g_vct;
 CRITICAL_SECTION g_csForVct;
 DWORD WINAPI ThreadFunc(LPVOID lpParam)
 {
 for (int i = 0; i < 0x1000; ++i) {
 EnterCriticalSection(&g_csForVct);
 g_vct.push_back(i);
 LeaveCriticalSection(&g_csForVct);
 }
 printf("tid:%08X counter:%08X \r\n", GetCurrentThreadId(), g_vct.size());
 return 0;
 }
 
 int main()
 {
 DWORD dwThreadId, dwThrdParam = 1;
 HANDLE hThread[2];
 InitializeCriticalSection(&g_csForVct);
 hThread[0] = CreateThread(
 NULL,
 0,
 ThreadFunc,
 &dwThrdParam,
 0,
 &dwThreadId);
 
 hThread[1] = CreateThread(
 NULL,
 0,
 ThreadFunc,
 &dwThrdParam,
 0,
 &dwThreadId);
 
 
 system("pause");
 cout << "Hello World!\n";
 DeleteCriticalSection(&g_csForVct);
 
 }
 
 
 
 | 
运行结果:

如果在main函数中添加EnterCriticalSection(&g_csForVct);
而没有LeaveCriticalSection(&g_csForVct); 
会使得两个进程都挂起 二者都在等待LeaveCriticalSection(&g_csForVct);的调用
调用几次EnterCriticalSection(&g_csForVct);
相应的就调用几次LeaveCriticalSection(&g_csForVct);
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 
 | int main(){
 DWORD dwThreadId, dwThrdParam = 1;
 HANDLE hThread[2];
 InitializeCriticalSection(&g_csForVct);
 
 hThread[0] = CreateThread(
 NULL,
 0,
 ThreadFunc,
 &dwThrdParam,
 0,
 &dwThreadId);
 
 hThread[1] = CreateThread(
 NULL,
 0,
 ThreadFunc,
 &dwThrdParam,
 0,
 &dwThreadId);
 
 system("pause");
 cout << "Hello World!\n";
 DeleteCriticalSection(&g_csForVct);
 
 }
 
 | 
内核同步对象
实现内核和三环之间的同步 由内核提供 可以做到跨进程同步
事件(Event)
| 12
 3
 4
 5
 
 | CreateEventSetEvent
 ResetEvent
 WaitForSingleObject
 WaitForMultipleObjects
 
 | 
WaitForSingleObject :
signaled(有信号)-开关, 开
nonsignaled(无信号) = 开关, 关
线程和进程内核对象:
 活着的时候,无信号
	 死了的时候,有信号
创建事件:


参数2:如果此参数为 TRUE,则函数将创建手动重置事件对象,该对象需要使用 ResetEvent函数将事件状态设置为无信号。 
如果此参数为 FALSE,则函数将创建一个自动重置事件对象,在释放单个等待线程后,系统会自动将事件状态重置为无信号。
将事件设置为有信号状态

将事件设置为无信号状态

等待内核同步对象 若内核同步对象为无信号就会阻塞 直到对象变为有信号 或者等到参数2的时间到

| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 
 | #include<Windows.h>#include <iostream>
 #include<vector>
 using namespace std;
 #pragma pack(1)
 
 vector<int> g_vct;
 HANDLE g_hEvent;
 DWORD WINAPI ThreadFunc(LPVOID lpParam)
 {
 for (int i = 0; i < 0x1000; ++i) {
 WaitForSingleObject(g_hEvent, INFINITE);
 g_vct.push_back(i);
 SetEvent(g_hEvent);
 }
 printf("tid:%08X counter:%08X \r\n", GetCurrentThreadId(), g_vct.size());
 
 return 0;
 }
 
 int main()
 {
 DWORD dwThreadId, dwThrdParam = 1;
 HANDLE hThread[2];
 
 g_hEvent = CreateEvent(NULL,
 FALSE,
 
 
 TRUE,
 NULL);
 
 hThread[0] = CreateThread(
 NULL,
 0,
 ThreadFunc,
 &dwThrdParam,
 0,
 &dwThreadId);
 
 hThread[1] = CreateThread(
 NULL,
 0,
 ThreadFunc,
 &dwThrdParam,
 0,
 &dwThreadId);
 
 
 system("pause");
 cout << "Hello World" << endl;
 }
 
 
 
 | 
区分WaitForSingleObject 是有信号还是超时:

等待多个信号(最多为64):

互斥体
| 12
 3
 
 | CreateMutex;OpenMutex;
 ReleaseMutex;
 
 | 
线程拥有权:任何线程在进入临界区之前,必须获取(acquire)与此区域相关联的互斥体的所有权。如果已有另一线程拥有了临界区的互斥体,其他线程就不能再进入其中。这些线程必须等待,直到当前的属主线程释放(release)该互斥体。
线程遗弃 :当拥有临界区的互斥体退出前,没有释放互斥体,那么系统会自动的将互斥体对象的线程ID设置为0,递归计数设置为0.等待函数返回WAIT_ABANDONED 而不是 WAIT_OBJECT_0 ,但是与互斥体不一样,事件的等待函数便会一直等待

参数2:当前创建互斥体的线程是否拥有互斥体的使用权


| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 
 | #include<Windows.h>#include <iostream>
 #include<vector>
 using namespace std;
 #pragma pack(1)
 
 
 vector<int> g_vct;
 HANDLE g_hMutex;
 DWORD WINAPI ThreadFunc(LPVOID lpParam)
 {
 for (int i = 0; i < 0x1000; ++i) {
 WaitForSingleObject(g_hMutex, INFINITE);
 g_vct.push_back(i);
 ReleaseMutex(g_hMutex);
 }
 printf("tid:%08X counter:%08X \r\n", GetCurrentThreadId(), g_vct.size());
 
 return 0;
 }
 
 int main()
 {
 DWORD dwThreadId, dwThrdParam = 1;
 HANDLE hThread[2];
 
 g_hMutex = CreateMutex(NULL,
 FALSE,
 
 NULL
 );
 
 WaitForSingleObject(g_hMutex, INFINITE);
 WaitForSingleObject(g_hMutex, INFINITE);
 ReleaseMutex(g_hMutex);
 ReleaseMutex(g_hMutex);
 hThread[0] = CreateThread(
 NULL,
 0,
 ThreadFunc,
 &dwThrdParam,
 0,
 &dwThreadId);
 
 hThread[1] = CreateThread(
 NULL,
 0,
 ThreadFunc,
 &dwThrdParam,
 0,
 &dwThreadId);
 
 
 system("pause");
 cout << "Hello World" << endl;
 }
 
 
 
 | 
