下面是我对Windows平台上的多线程开发的一些理解,具体的可以看代码:
0 线程直接相关的函数是:
- ResumeThread;
- SuspendThread;
- TerminateThread;
1 互斥控制是用互斥锁:Mutex
- CreateMutex;
- OpenMutex;
- ReleaseMutex;
2 同步控制是用信号量:Semaphore
- CreateSemaphore;
- OpenSemaphore;
- ReleaseSemaphore;
3 互斥也是一种同步,即同步包含互斥,据说还有一个专门用来表示互斥的结构体,目前没有用过。
4 创建线程的时候可以传参,一定要注意保护参数。
5 多线程并发时的输入输出注意保护其互斥性,避免输出混乱。
6 对线程传参如果参数复杂的话,最好提前定义一个数据结构,下面我的代码中并没有去定义一个很好的数据结构。
7 对线程传参时,要把信号量名字传过去,或者定义成全局变量,最好,是采用6的方案
8 除了常用的WaitForSingleObject,还有WaitForMultipleObjects,不过后者目前我还没有用过。
9 以上提到的函数是在控制台程序和WIN32窗口程序中用的,而在MFC中,另有线程类CWinThread和信号量类CSemaphore等等。
10 进程/线程同步有四种方法:
- 临界区,只能用于进程内部的线程之间同步
- CRITICAL_SECTION cs; //临界区结构体
- InitializeCriticalSection(&cs); //初始化临界区
- EnterCriticalSection(&cs); //进入临界区
- LeaveCriticalSection(&cs); //离开临界区
- DeleteCriticalSection(&cs); //删除临界区
- event,可以用于进程间同步
- mutex,可以用于进程间同步
- semaphore,可以用于进程间同步
代码:
1 #include <windows.h> 2 #include <time.h> 3 #include <iostream> 4 #include <assert.h> 5 6 /** 7 * @author:zanzan101 8 */ 9 10 using namespace std; 11 12 #define MAX 20 13 14 void thinking(char para, HANDLE mutex) 15 { 16 // 这里需要互斥,不然都打印混了 17 WaitForSingleObject(mutex, INFINITE); 18 cout<< para << " is thinking ..." << endl; 19 ReleaseMutex(mutex); 20 Sleep(rand()%100); 21 } 22 23 void eating(char para, HANDLE mutex) 24 { 25 // 这里需要互斥,不然都打印混了 26 WaitForSingleObject(mutex, INFINITE); 27 cout<< para << " is eating ..." << endl; 28 ReleaseMutex(mutex); 29 Sleep(rand()%1000); 30 } 31 32 DWORD __stdcall philosopher(void* para) 33 { 34 // 注:Open系列的函数,如果第一个参数是0,则创建出来的都是NULL 35 char ch; 36 char print_mutex_name[] = "print_mutex"; 37 HANDLE hPrintMutex = OpenMutex(MUTEX_ALL_ACCESS, 0, print_mutex_name); 38 39 char chopstick_semaphore_name[] = "chopstick_semaphore"; 40 HANDLE hCountSemaphore = OpenSemaphore(SEMAPHORE_ALL_ACCESS, 0, chopstick_semaphore_name); 41 42 char chopstick_mutex_name[] = "X_chopstick"; 43 chopstick_mutex_name[0] = *(char*)para; 44 HANDLE hChopstick1 = OpenMutex(MUTEX_ALL_ACCESS, 0, chopstick_mutex_name); 45 chopstick_mutex_name[0] = (*(char*)para-'A'+1)%MAX + 'A'; 46 HANDLE hChopstick2 = OpenMutex(MUTEX_ALL_ACCESS, 0, chopstick_mutex_name); 47 48 49 // 这里测试输出了子线程栈的起始地址,发现子线程的起始地址按照设定的大小逐倍向高地址增长 50 // 即后面的线程的栈起始地址,比前面线程栈的起始地址的差距,为创建线程时设定的栈大小 51 WaitForSingleObject(hPrintMutex, INFINITE); 52 cout << *(char*)para << "\'s stack size is " << ((unsigned int(&ch))>>10) << " KB" << endl; 53 ReleaseMutex(hPrintMutex); 54 55 while(1) 56 { 57 thinking(*(char*)para, hPrintMutex); 58 59 WaitForSingleObject(hPrintMutex, INFINITE); 60 cout << *(char*)para << " end thinking ..." << endl; 61 ReleaseMutex(hPrintMutex); 62 63 // 打破死锁的方法之一 64 // 限制同时吃饭的人数 65 // 当一共有N个人时,我们限制最多同时有N-1个人请求持筷子,这样必然打破环路等待条件 66 #if 1 67 WaitForSingleObject(hCountSemaphore, INFINITE); 68 #endif 69 70 WaitForSingleObject(hChopstick1, INFINITE); 71 72 WaitForSingleObject(hPrintMutex, INFINITE); 73 cout << *(char*)para << " get first chopstick ..." << endl; 74 ReleaseMutex(hPrintMutex); 75 76 // 为了测试死锁,故意增大拿两个筷子的间隔,以提高死锁的概率。 77 Sleep(rand()%1000); 78 WaitForSingleObject(hChopstick2, INFINITE); 79 80 WaitForSingleObject(hPrintMutex, INFINITE); 81 cout << *(char*)para << " get second chopstick ..." << endl; 82 ReleaseMutex(hPrintMutex); 83 84 #if 1 85 ReleaseSemaphore(hCountSemaphore, 1, NULL); 86 #endif 87 88 eating(*(char*)para, hPrintMutex); 89 90 WaitForSingleObject(hPrintMutex, INFINITE); 91 cout << *(char*)para << " end eating ..." << endl; 92 ReleaseMutex(hPrintMutex); 93 94 ReleaseMutex(hChopstick1); 95 ReleaseMutex(hChopstick2); 96 97 WaitForSingleObject(hPrintMutex, INFINITE); 98 cout << *(char*)para << " put down chopsticks ..." << endl; 99 ReleaseMutex(hPrintMutex); 100 } 101 } 102 103 int main() 104 { 105 assert(MAX <= 26); 106 char ch; 107 108 char str[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 109 HANDLE hMutex[MAX]; 110 HANDLE hPrintMutex; 111 HANDLE hCountSemaphore; 112 char chopstick_mutex_name[] = "X_chopstick"; 113 for (int i = 0; i < MAX; i++) 114 { 115 chopstick_mutex_name[0] = str[i]; 116 117 // 创建筷子的互斥锁 118 hMutex[i] = CreateMutex(0, NULL, chopstick_mutex_name); 119 } 120 hPrintMutex = CreateMutex(0, NULL, "print_mutex"); 121 hCountSemaphore = CreateSemaphore(NULL, 4, 4, "chopstick_semaphore"); 122 HANDLE philosophers[MAX]; 123 int count = MAX; 124 srand(time(0)); 125 126 WaitForSingleObject(hPrintMutex, INFINITE); 127 cout << "main's stack size is " << (unsigned int(&ch))/1024 << " KB" << endl; 128 ReleaseMutex(hPrintMutex); 129 130 for(int i = 0; i < MAX; i++) 131 { 132 // 创建线程 133 // 线程栈大小设置为1K 134 // 要尽量保证在传址之后,在该线程的生命周期中,不去改变该地址的数据,以免多线程并发时出现意外 135 // 这里我对每个线程传参不同,而且后面不再修改这个地址,做到了上面这一点 136 philosophers[i] = CreateThread(NULL, 1>>10, philosopher, (void*)&str[i], 0, NULL); 137 } 138 139 Sleep(2000); 140 141 // 如果不挂起子线程,主线程暂停时,子线程继续运行,不受任何影响 142 // 因此,这里需要挂起子线程,暂停子线程 143 for(int i = 0; i < count; i++) 144 SuspendThread(philosophers[i]); 145 system("pause"); 146 147 // 下面这里,如果终止的线程,后面就无法再次启动线程了,因此这里不能终止线程 148 // for(int i = 0; i < count; i++) 149 // TerminateThread(philosophers[i], 0); 150 151 // 再次启动线程 152 for(int i = 0; i < count; i++) 153 ResumeThread(philosophers[i]); 154 155 Sleep(2000); 156 // 再次挂起线程 157 for(int i = 0; i < count; i++) 158 SuspendThread(philosophers[i]); 159 160 system("pause"); 161 162 // 终止线程 163 for(int i = 0; i < count; i++) 164 TerminateThread(philosophers[i], 0); 165 // 关闭句柄 166 for(int i = 0; i < count; i++) 167 CloseHandle(philosophers[i]); 168 return 0; 169 }
输出结果:
main's stack size is 1215 KB A's stack size is 5247 KB B's stack size is 6271 KB D's stack size is 8319 KB F's stack size is 10367 KB J's stack size is 14463 KB C's stack size is 7295 KB E's stack size is 9343 KB G's stack size is 11391 KB I's stack size is 13439 KB K's stack size is 15487 KB M's stack size is 17535 KB S's stack size is 23679 KB R's stack size is 22655 KB P's stack size is 20607 KB Q's stack size is 21631 KB N's stack size is 18559 KB O's stack size is 19583 KB H's stack size is 12415 KB T's stack size is 24703 KB L's stack size is 16511 KB A is thinking ... B is thinking ... D is thinking ... F is thinking ... J is thinking ... C is thinking ... E is thinking ... G is thinking ... I is thinking ... K is thinking ... M is thinking ... S is thinking ... R is thinking ... P is thinking ... Q is thinking ... N is thinking ... O is thinking ... H is thinking ... T is thinking ... L is thinking ... A end thinking ... A get first chopstick ... B end thinking ... D end thinking ... F end thinking ... B get first chopstick ... J end thinking ... D get first chopstick ... C end thinking ... F get first chopstick ... E end thinking ... G end thinking ... I end thinking ... K end thinking ... M end thinking ... S end thinking ... R end thinking ... P end thinking ... Q end thinking ... N end thinking ... O end thinking ... H end thinking ... T end thinking ... L end thinking ... B get second chopstick ... B is eating ... J get first chopstick ... D get second chopstick ... F get second chopstick ... D is eating ... F is eating ... B end eating ... B put down chopsticks ... A get second chopstick ... C get first chopstick ... B is thinking ... A is eating ... D end eating ... F end eating ... B end thinking ... D put down chopsticks ... E get first chopstick ... F put down chopsticks ... G get first chopstick ... D is thinking ... F is thinking ... D end thinking ... F end thinking ... J get second chopstick ... J is eating ... I get first chopstick ... A end eating ... A put down chopsticks ... A is thinking ... A end thinking ... J end eating ... J put down chopsticks ... J is thinking ... C get second chopstick ... J end thinking ... C is eating ... K get first chopstick ... E get second chopstick ... E is eating ... M get first chopstick ... G get second chopstick ... G is eating ... S get first chopstick ... I get second chopstick ... I is eating ... R get first chopstick ... C end eating ... C put down chopsticks ... C is thinking ... C end thinking ... E end eating ... E put down chopsticks ... E is thinking ... E end thinking ... G end eating ... G put down chopsticks ... G is thinking ... G end thinking ... I end eating ... I put down chopsticks ... I is thinking ... I end thinking ... K get second chopstick ... K is eating ... P get first chopstick ... M get second chopstick ... M is eating ... Q get first chopstick ... S get second chopstick ... S is eating ... 请按任意键继续. . . K end eating ... M end eating ... S end eating ... K put down chopsticks ... M put down chopsticks ... N get first chopstick ... S put down chopsticks ... R get second chopstick ... K is thinking ... M is thinking ... S is thinking ... R is eating ... A get first chopstick ... K end thinking ... M end thinking ... S end thinking ... A get second chopstick ... A is eating ... R end eating ... R put down chopsticks ... Q get second chopstick ... R is thinking ... Q is eating ... C get first chopstick ... R end thinking ... N get second chopstick ... N is eating ... D get first chopstick ... D get second chopstick ... D is eating ... Q end eating ... Q put down chopsticks ... P get second chopstick ... Q is thinking ... P is eating ... F get first chopstick ... Q end thinking ... N end eating ... N put down chopsticks ... N is thinking ... N end thinking ... F get second chopstick ... F is eating ... A end eating ... A put down chopsticks ... B get first chopstick ... A is thinking ... A end thinking ... P end eating ... P put down chopsticks ... P is thinking ... P end thinking ... D end eating ... D put down chopsticks ... C get second chopstick ... E get first chopstick ... D is thinking ... C is eating ... H get first chopstick ... D end thinking ... F end eating ... F put down chopsticks ... E get second chopstick ... G get first chopstick ... F is thinking ... E is eating ... I get first chopstick ... F end thinking ... I get second chopstick ... I is eating ... 请按任意键继续. . .