前两篇多线程笔记可以不用看,这个是更系统更详细的整理。 每次使用多线程时,总有些细节问题不清楚,这里从基础部分开始整理一下,以便后续进行学习和使用。 机器不同,系统给每个线程分配的时间片和运行机制也不同。我这里是基于win10系统的VS2010的win32控制台应用程序做的。运行结果,会与孙鑫视频课中的有些不同,所以有些代码稍微调整,比如Sleep(1)的位置。 当sleep 10ms时,线程能完整输出。当sleep 1ms 时,线程的回车不能输出。 如果线程输出内容比较多,还会被截断。所以需要控制好主线程的Sleep()的时间。 系统不同,那么每个线程得到的时间片长短也不同。 从结果,可以看出,两个时间片由系统分配,然后主线程和线程,按照各自得到的时间片交替输出。 运行结果如下。当tickets=1时,进入1线程,1线程Sleep(1), 2线程开始卖票,然后2线程Sleep(1), 又将执行权交给1线程, 1线程卖完,剩0张票, 所以2线程,就卖0票了。但是在运行过程中,并没有出现这个结果,可能是系统自己处理了最后的判断。定位跟踪了一下,发现确实,到tickets=1时,Sleep(1);就被跳过了,不再睡眠,直接在这一个线程内执行完毕。 虽然不会出现买0张票,当设置tickets=3,单步跟踪时,出现了两个线程同时买2号票,所以还是有问题的。为了安全起见,还是需要互斥或者线程锁的,下面依次进行介绍三种互斥或线程锁的方法。 HANDLE hMutex; 相当于一把钥匙,谁拿到了,就先用,等用完了,把钥匙还给系统,下一个线程再取钥匙,进行使用。 hMutex = CreateMutex(NULL, FALSE, NULL);//初始的时候,创建的钥匙,要设置为FALSE,就是谁也没有它的拥有权。如果不小心,设置成了TRUE,那么就需要紧跟其后释放一下线程:ReleaseMutex(hMutex);即谁拥有谁释放。 WaitForSingleObject(hMutex, INFINITE);// 某线程,拿到钥匙的使用权。 …// 执行需要进行的内容 ReleaseMutex(hMutex);// 使用完之后,要释放线程的使用权,否则别的线程请求不到,就堵塞了。 运行结果:完美的交替运行。 运行结果:交替进行,直到将票卖完。 用关键代码段,来控制输出,使得每个线程买一部分票。但是结果:全部都是1线程在卖票,不管是否设置sleep。后来调整Sleep的位置,得到想到的交替进行的结果。 不知道系统内部是如何调用的这个关键代码段标记。跟教科书上不一样。所以最终是不建议用这个。 如果没有运行完线程,就调用:DeleteCriticalSection(§ion); 则会出现如下问题。 正确运行结果:将Sleep(1); 放在关键代码段结束之后。 至此,多线程使用的几种方法整理完毕。至于在多线程中,使用哪个线程,需要根据情况而定。通过这一次的对比,个人还是更倾向于第一种互斥。一、最简单的多线程原型
#include <Windows.h>// 使用系统API函数,所以需要包含此头文件 #include <iostream> using namespace std; DWORD WINAPI Fun1Proc(LPVOID lpParameter) { cout << "thread1 is running" << endl; return 0; } int main() { HANDLE hThread1; hThread1 = CreateThread(NULL, 0, Fun1Proc, NULL, 0, NULL); // 创建之后,关闭线程句柄。 // 表明主线程不需要引用刚创建的线程,但是线程依然在运行。 // 让线程的引用计数减一,当线程结束,计数就能为0,自动清空释放。 // 否则只能等整个进程计数后,才释放内核对象 CloseHandle(hThread1); // 这个时间,是为了让线程输出,如果设置的小,不等线程输出完,主线程就会先输出了,然后线程继续输出剩下的部分。 Sleep(10); cout << "main thread!" << endl; system("pause"); return 0; }
二、测试系统为多线程分配的时间片
#include <Windows.h>// 使用系统API函数,所以需要包含此头文件 #include <iostream> using namespace std; int index = 1; DWORD WINAPI Fun1Proc(LPVOID lpParameter) { while(index++ < 1000) { cout << "thread1 is running" << endl; } return 0; } int main() { HANDLE hThread1; hThread1 = CreateThread(NULL, 0, Fun1Proc, NULL, 0, NULL); // 创建之后,关闭线程句柄。 // 表明主线程不需要引用刚创建的线程,但是线程依然在运行。 // 让线程的引用计数减一,当线程结束,计数就能为0,自动清空释放。 // 否则只能等整个进程计数后,才释放内核对象 CloseHandle(hThread1); // 这个时间,是为了让线程输出,如果设置的小,不等线程输出完,主线程就会先输出了,然后线程继续输出剩下的部分。 // Sleep(10); while(index++ < 1000) cout << "main thread!" << endl; system("pause"); return 0; }
三、开辟两个线程,模拟售卖火车票系统
#include <Windows.h>// 使用系统API函数,所以需要包含此头文件 #include <iostream> using namespace std; int tickets = 100; DWORD WINAPI Fun1Proc(LPVOID lpParameter) { while(true) { if (tickets > 0) { Sleep(1); cout << "thread1 sell ticket : " << tickets--<< endl; } else { break; } } return 0; } DWORD WINAPI Fun2Proc(LPVOID lpParameter) { while(true) { if (tickets > 0) { Sleep(1); cout << "thread2 sell ticket : " << tickets--<< endl; } else { break; } } return 0; } int main() { HANDLE hThread1, hThread2; hThread1 = CreateThread(NULL, 0, Fun1Proc, NULL, 0, NULL); hThread2 = CreateThread(NULL, 0, Fun2Proc, NULL, 0, NULL); CloseHandle(hThread1); CloseHandle(hThread2); Sleep(1000);// 让上面两个线程运行完之后,主线程再结束 system("pause"); return 0; }
四、线程互斥CreateMutex
#include <Windows.h>// 使用系统API函数,所以需要包含此头文件 #include <iostream> using namespace std; int tickets = 100; HANDLE hMutex; // 两个线程都会用到,所以需要声明为全局变量 DWORD WINAPI Fun1Proc(LPVOID lpParameter) { while(true) { WaitForSingleObject(hMutex, INFINITE);// 直到有信号,否则一直等待,不设定超时 if (tickets > 0) { Sleep(1); cout << "thread1 sell ticket : " << tickets--<< endl; } else { break; } ReleaseMutex(hMutex);// 释放指定互斥对象的所有权,如果不释放,线程2无法请求 } return 0; } DWORD WINAPI Fun2Proc(LPVOID lpParameter) { while(true) { WaitForSingleObject(hMutex, INFINITE); if (tickets > 0) { Sleep(1); cout << "thread2 sell ticket : " << tickets--<< endl; } else { break; } ReleaseMutex(hMutex); } return 0; } int main() { HANDLE hThread1, hThread2; hThread1 = CreateThread(NULL, 0, Fun1Proc, NULL, 0, NULL); hThread2 = CreateThread(NULL, 0, Fun2Proc, NULL, 0, NULL); CloseHandle(hThread1); CloseHandle(hThread2); //一开始为FALSE,表明没有线程拥有这个互斥对象 hMutex = CreateMutex(NULL, FALSE, NULL); Sleep(1000); system("pause"); return 0; }
五、线程同步——创建事件对象CreateEvent
#include <Windows.h>// 使用系统API函数,所以需要包含此头文件 #include <iostream> using namespace std; int tickets = 100; HANDLE hEvent; // 事件对象 DWORD WINAPI Fun1Proc(LPVOID lpParameter) { while(true) { WaitForSingleObject(hEvent, INFINITE);// 直到有信号,否则一直等待,不设定超时 if (tickets > 0) { Sleep(1); cout << "thread1 sell ticket : " << tickets--<< endl; } else { break; } SetEvent(hEvent); } return 0; } DWORD WINAPI Fun2Proc(LPVOID lpParameter) { while(true) { WaitForSingleObject(hEvent, INFINITE);// 请求事件对象,将hEvent设置为非信号状态 if (tickets > 0) { Sleep(1); cout << "thread2 sell ticket : " << tickets--<< endl; } else { break; } SetEvent(hEvent);// 将hEvent设置为有信号状态,其他线程可拿去使用 } return 0; } int main() { HANDLE hThread1, hThread2; hThread1 = CreateThread(NULL, 0, Fun1Proc, NULL, 0, NULL); hThread2 = CreateThread(NULL, 0, Fun2Proc, NULL, 0, NULL); CloseHandle(hThread1); CloseHandle(hThread2); // 第二个参数:TRUE,表明人工重置对象, FALSE,非人工,即自动重置对象 // 第三个参数:FALSE,表明一开始没有线程拥有这个hEvent;无信号状态 hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); SetEvent(hEvent);// 使得hEvent为有信号状态,其他线程可以取到 Sleep(1000); CloseHandle(hEvent);// 最后需要关闭事件句柄 system("pause"); return 0; }
六、线程同步——关键代码段
#include <Windows.h>// 使用系统API函数,所以需要包含此头文件 #include <iostream> using namespace std; int tickets = 1000;// 两个线程共同卖票 CRITICAL_SECTION section;// 临界区对象 DWORD WINAPI Fun1Proc(LPVOID lpParameter) { while(true) { EnterCriticalSection(§ion);// 进入临界区,获取所有权 if (tickets > 0) { //Sleep(2);// 不管用 cout << "thread1 sell ticket : " << tickets-- << endl; } else { break; } LeaveCriticalSection(§ion);// 释放所有权 Sleep(1);// 会得到正解 } return 0; } DWORD WINAPI Fun2Proc(LPVOID lpParameter) { while(true) { EnterCriticalSection(§ion); if (tickets > 0) { //Sleep(2);// 不管用 cout << "thread2 sell ticket : " << tickets-- << endl; } else { break; } LeaveCriticalSection(§ion); Sleep(1);// 会得到正解 } return 0; } int main() { InitializeCriticalSection(§ion);// 用之前,必须初始化 HANDLE hThread1, hThread2; hThread1 = CreateThread(NULL, 0, Fun1Proc, NULL, 0, NULL); hThread2 = CreateThread(NULL, 0, Fun2Proc, NULL, 0, NULL); CloseHandle(hThread1); CloseHandle(hThread2); Sleep(4000);// 设置的时间一定要等线程运行完,否则,下面的代码会出现问题 DeleteCriticalSection(§ion);// 程序都运行完成之后,释放 system("pause"); return 0; }
本网页所有视频内容由 imoviebox边看边下-网页视频下载, iurlBox网页地址收藏管理器 下载并得到。
ImovieBox网页视频下载器 下载地址: ImovieBox网页视频下载器-最新版本下载
本文章由: imapbox邮箱云存储,邮箱网盘,ImageBox 图片批量下载器,网页图片批量下载专家,网页图片批量下载器,获取到文章图片,imoviebox网页视频批量下载器,下载视频内容,为您提供.
阅读和此文章类似的: 全球云计算