1. 程式人生 > >C++ 練習-多執行緒

C++ 練習-多執行緒

#if 0  //CreateThread程式碼示例
#include<iostream>
#include<windows.h>
using namespace std;
DWORD WINAPI ThreadFun(LPVOID lpThreadParameter)
{
    char* str = (char*)lpThreadParameter;
    while (true)
    {
        cout << "執行緒處理函式中接收到的引數是:" << str << endl;
        cout << "子執行緒ID: "
<< GetCurrentThreadId() << endl; Sleep(1000); } return 0; } int main() { DWORD threadId = 0; HANDLE hThread = CreateThread( NULL, //設為NULL表示預設安全性 0, //如果設為0,那麼預設將使用與呼叫該函式的執行緒相同的棧空間大小 ThreadFun,//執行緒處理函式,函式名就是函式指標 "hello thread!"
,//向執行緒函式傳入的引數 0, //0表示建立後馬上執行 &threadId); if (hThread == NULL){ cout << "執行緒建立失敗,ERROR CODE : " << GetLastError() << endl; } cout << "執行緒的控制代碼:" << hThread << endl; cout << "子執行緒的ID:" << threadId << endl; cout
<< "主執行緒的ID:" << GetCurrentThreadId() << endl; //關閉執行緒控制代碼,引用計數-1,並沒有結束執行緒 //CloseHandle(hThread);//表示以後不再引用控制代碼 getchar(); //掛起執行緒 SuspendThread(hThread); getchar(); //恢復執行執行緒 ResumeThread(hThread); getchar(); return 0; } #endif #if 0//多執行緒間訊息通訊 #include<iostream> #include<windows.h> #include<stdio.h> //自定義訊息必須自定義在使用者訊息以上的數值 #define MY_MSG WM_USER+1 bool flag = true; DWORD WINAPI ThreadFun1(LPVOID param) { //接收第二個執行緒發來的訊息 MSG msg; #if 0 //GetMessage 如果有訊息繼續,否則阻塞 while (GetMessage( &msg,//接收訊息的執行緒ID NULL,//取得訊息的視窗的控制代碼,當為NULL時獲取所屬執行緒的訊息 0, //指定被檢索的最小訊息值的整數 0 //指定被檢索的最大訊息值的整數 )) { switch (msg.message){ case MY_MSG: printf("收到訊息:%d\n", (int)msg.wParam); break; //case WM_QUIT: // printf("收到WM_QUIT訊息,退出!\n");//因為WM_QUIT訊息會讓GetMessage返回false所以不會進人迴圈 // break; } } printf("收到WM_QUIT訊息,退出!\n"); #endif #if 1 //PeekMessage的用法 不阻塞執行緒 while (flag) { if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)){//得到訊息返回非零,否則返回0值 switch (msg.message){ case MY_MSG: printf("收到訊息:%d\n", (int)msg.wParam); break; case WM_QUIT: printf("收到QUIT訊息,退出!\n"); flag = false; break; } } } #endif return 0; } DWORD WINAPI ThreadFun2(LPVOID param) { //給第一個執行緒傳送訊息 DWORD threadId = (DWORD)param; int N = 1; while (TRUE){ if (N <= 3){ PostThreadMessage(threadId, MY_MSG, (WPARAM)N++, NULL); } else{ PostThreadMessage(threadId, WM_QUIT, NULL, NULL); break; } Sleep(1000); } return 0; } int main() { DWORD threadID = 0; HANDLE hthread1 = CreateThread(NULL, 0, ThreadFun1, NULL, 0, &threadID); HANDLE hthread2 = CreateThread(NULL, 0, ThreadFun2, (LPVOID)threadID, 0, NULL); HANDLE hArr[] = { hthread1, hthread2 }; WaitForMultipleObjects(2, hArr, TRUE, INFINITE); //getchar(); system("pause"); return 0; } #endif #if 0//靜態TLS(執行緒本地儲存變數) #include<iostream> #include<Windows.h> #include<stdio.h> //宣告為靜態TLS(執行緒本地儲存變數) __declspec(thread) int N = 0;//每個執行緒中的值互不影響 DWORD WINAPI ThreadFun1(LPVOID param){ char *name = reinterpret_cast<char *>(param);//()還不能少 while (TRUE){ printf("執行緒%s列印:%d\n", name, ++N); Sleep(1000); } } DWORD WINAPI ThreadFun2(LPVOID param){ char *name = reinterpret_cast<char *>(param);//()還不能少 while (TRUE){ printf("\t\t\t執行緒%s列印:%d\n", name, ++(++N)); Sleep(1000); } } void main(){ HANDLE hThread1 = CreateThread(NULL, 0, ThreadFun1, "thread1", 0, NULL); HANDLE hThread2 = CreateThread(NULL, 0, ThreadFun2, "thread2", 0, NULL); HANDLE hArr[] = { hThread1, hThread2 }; WaitForMultipleObjects(2, hArr, TRUE, INFINITE); system("pause"); } #endif #if 0//動態TLS(Thread Local Storage) #include<iostream> #include<stdio.h> #include<Windows.h> DWORD WINAPI ThreadFun1(LPVOID param); DWORD WINAPI THreadFun2(LPVOID param); //動態TLS的索引 DWORD tlsIndex = 0; int main() { tlsIndex = TlsAlloc();//返回索引 if (tlsIndex == TLS_OUT_OF_INDEXES){ printf("分配TLS索引失敗!\n"); return 0; } //在主執行緒中設定一個值 TlsSetValue(tlsIndex, "main cpp"); char *pName = (char*)TlsGetValue(tlsIndex); printf("主執行緒列印: %s\n", pName); HANDLE hThread1 = CreateThread(NULL, 0, ThreadFun1, "A", 0, NULL); HANDLE hThread2 = CreateThread(NULL, 0, THreadFun2, "B", 0, NULL); HANDLE hArr[] = { hThread1, hThread2 }; WaitForMultipleObjects(2, hArr, TRUE, INFINITE);//等待兩個執行緒執行完畢 TlsFree(tlsIndex); system("pause"); return 0; } DWORD WINAPI ThreadFun1(LPVOID param){ TlsSetValue(tlsIndex, "hello"); while (true) { char *p = (char*)TlsGetValue(tlsIndex); printf("執行緒A列印:%s\n", p); Sleep(1000); } } DWORD WINAPI THreadFun2(LPVOID param){ TlsSetValue(tlsIndex, "world"); while (true) { char *p = reinterpret_cast<char *>(TlsGetValue(tlsIndex)); printf("\t\t\t執行緒B列印:%s\n", p); Sleep(1000); } } #endif #if 0//利用事件Event實現買票程式 #include<iostream> #include<Windows.h> #include<stdio.h> #include<process.h>//_beginthread void __cdecl SellThread1(void* param); void __cdecl SellThread2(void* param); int tickets = 100; //個INVALID_HANDLE_VALUE ( vs2008下為0xfffffffff ) 實際值等於-1 //INVALID_HANDLE_VALUE類似與指標裡的NULL,如果將指標釋放後,應該立即將指標賦為NULL,否則出現野指標; //同理,控制代碼執行closehandle後,應該立即將控制代碼置為INVALID_HANDLE_VALUE,即讓控制代碼失效。 HANDLE hEvent = INVALID_HANDLE_VALUE; int main() { //建立事件,此刻為有訊號狀態,自動重置訊號狀態,初始化為有訊號狀態,執行緒可以直接獲取 hEvent = CreateEvent(NULL, FALSE, TRUE, L"事件物件"); Sleep(1000); //主執行緒休眠1秒後,將訊號量置為無訊號狀態 //ResetEvent(hEvent); printf("開始賣票了!\n"); //建立兩個售票執行緒 uintptr_t t1 = _beginthread(SellThread1, 0, "售票視窗A"); uintptr_t t2 = _beginthread(SellThread2, 0, "售票視窗B"); //無限等待兩個執行緒全部執行完畢 HANDLE hArr[] = { (HANDLE)t1, (HANDLE)t2 }; WaitForMultipleObjects(2, hArr, TRUE, INFINITE); printf("賣票結束!\n"); system("pause"); return 0; } void __cdecl SellThread1(void* param){ char *name = reinterpret_cast<char*>(param); while (tickets > 0){ //如果事件物件為訊號狀態(沒有執行緒擁有它),則執行緒可以獲取它後繼續執行 //自動重置的事件物件,呼叫了WaitForSingleObject函式之後,自動重置為無訊號, //即其他執行緒不能再搶佔了 WaitForSingleObject(hEvent, INFINITE); if (tickets > 0){ Sleep(10); } //讓事件變成有訊號狀態,相當於解鎖 SetEvent(hEvent); } } void __cdecl SellThread2(void* param){ char *name = reinterpret_cast<char*>(param); while (tickets > 0){ WaitForSingleObject(hEvent, INFINITE); if (tickets > 0){ Sleep(10); printf("%s賣出第%d張票!\n", name, tickets--); } SetEvent(hEvent); } } #endif #if 0//利用事件物件實現一個程式只允許執行一個程序 #include<iostream> #include<windows.h> #include<stdio.h> int main() { HANDLE hEvent = CreateEvent(NULL, FALSE, TRUE, L"事件物件"); if (GetLastError() == ERROR_ALREADY_EXISTS){ printf("程式已經運行了,退出!\n"); getchar(); CloseHandle(hEvent); return 0; } printf("程式第一次執行!\n"); getchar(); return 0; } #endif #if 0//利用semaphore實現程式只允許一個程序執行 #include<iostream> #include<Windows.h> #include<stdio.h> int main() { HANDLE hSemaphore = CreateSemaphore(NULL, 3, 3, L"停車位"); if (GetLastError() == ERROR_ALREADY_EXISTS){ printf("程式已經執行,請不要開啟多個程序!\n"); getchar(); CloseHandle(hSemaphore); return 0; } printf("程式第一次執行!\n"); getchar(); CloseHandle(hSemaphore); return 0; } #endif #if 0 //採用互斥體來實現單程序執行 #include<iostream> #include<Windows.h> #include<stdio.h> int main() { HANDLE hMutex = CreateMutex(NULL, FALSE, L"售票互斥體"); if (GetLastError() == ERROR_ALREADY_EXISTS){ printf("程式已經執行,退出\n"); getchar(); CloseHandle(hMutex); return 0; } printf("第一次執行程式!\n"); getchar(); return 0; } #endif #if 0 //訊號量控制停車場車位 #include<windows.h> #include<stdio.h> DWORD WINAPI ThreadFun(LPVOID param); struct Car{ char name[20]; DWORD time; }; HANDLE hSemphore = INVALID_HANDLE_VALUE; int main() { //初始化三個停車位資源 hSemphore = CreateSemaphore(NULL, 3, 3, L"停車場"); HANDLE hArr[5] = { INVALID_HANDLE_VALUE }; for (int i = 0; i < 5; ++i){ Car *pCar = new Car; sprintf_s(pCar->name, "車輛%c", 'A' + i); pCar->time = 3 + i * 3; //建立車輛執行緒 hArr[i] = CreateThread(NULL, 0, ThreadFun, (LPVOID)pCar, 0, NULL); } //等待所有執行緒執行完畢 WaitForMultipleObjects(5, hArr, true, INFINITE); return 0; } DWORD WINAPI ThreadFun(LPVOID param) { //如果有剩餘車位資源(有訊號狀態),就放行(往下執行) WaitForSingleObject(hSemphore, INFINITE); Car *pCar = reinterpret_cast<Car*>(param); printf("%s進入停車場,停車%d秒!\n", pCar->name, pCar->time); Sleep(pCar->time * 1000); printf("%s離開停車場!\n", pCar->name); //釋放一個停車位(訊號量+1) ReleaseSemaphore(hSemphore, 1, NULL); return 0; } #endif #if 0//互斥體 售票 #include<iostream> #include<process.h> #include<stdio.h> #include<windows.h> void __cdecl SellThread1(void* param); void __cdecl SellThread2(void* param); int tickets = 100; HANDLE hMutex = INVALID_HANDLE_VALUE; int main() { hMutex = CreateMutex(NULL, FALSE, L"售票互斥體"); printf("開始賣票了!\n"); uintptr_t t1 = _beginthread(SellThread1, 0, "售票視窗A"); uintptr_t t2 = _beginthread(SellThread2, 0, "售票視窗B"); HANDLE hArr[] = { (HANDLE)t1, (HANDLE)t2 }; WaitForMultipleObjects(2, hArr, true, INFINITE); printf("賣票結束!\n"); system("pause"); return 0; } void __cdecl SellThread1(void* param){ char *name = reinterpret_cast<char *>(param); while (tickets > 0){ //如果這個互斥體為有訊號狀態(沒有執行緒擁有它),則執行緒獲取它後繼續執行 WaitForSingleObject(hMutex, INFINITE); if (tickets > 0){ Sleep(10); //CPU恰好執行到這裡,這個時候執行緒的時間片到了,並且此時還剩最後一張票 printf("%s賣出第%d張票!\n", name, tickets--); } //釋放對互斥體的擁有權 ,它變成有訊號狀態 ReleaseMutex(hMutex); } } void __cdecl SellThread2(void* param){ char *name = reinterpret_cast<char *>(param); while (tickets > 0){ WaitForSingleObject(hMutex, INFINITE); if (tickets > 0){ Sleep(10); printf("%s賣出第%d張票!\n", name, tickets--); } ReleaseMutex(hMutex); } } #endif #if 0 //pv操作 生產者 消費者模式 P操作指:通過 V操作指:釋放 #include<windows.h> #include<queue> #include<process.h> using namespace std; queue<int> store; int StoreSize = 3;//倉庫可以放3個貨物 int ID = 1;//貨物起始ID //隨機時間陣列,模擬隨機生產和消費的速度 int arr1[10] = { 2, 1, 3, 5, 9, 2, 5, 2, 3, 7 }; //需要兩個Event來通知 HANDLE hEvent1 = INVALID_HANDLE_VALUE;//有貨物時通知消費者去取貨物 HANDLE hEvent2= INVALID_HANDLE_VALUE;//倉庫有空時通知生產者開始生產 //生產者 void ProducerThread(LPVOID param); //消費者 void ConsumerThread(LPVOID param); int main() { //需要先開始生產 hEvent1 = CreateEvent(NULL, TRUE, TRUE, L"事件物件1"); //一開始,倉庫沒貨取 hEvent2 = CreateEvent(NULL, TRUE, FALSE, L"事件物件2"); uintptr_t t1 = _beginthread(ProducerThread, 0, NULL); uintptr_t t2 = _beginthread(ConsumerThread, 0, NULL); //無限等待兩個執行緒執行結束 HANDLE hArr[] = { (HANDLE)t1, (HANDLE)t2 }; WaitForMultipleObjects(2, hArr, TRUE, INFINITE); CloseHandle(hEvent1); CloseHandle(hEvent2); system("pause"); return 0; } //生產者 void ProducerThread(LPVOID param){ while (true) { WaitForSingleObject(hEvent1, INFINITE);//看event是否允許生產 if (store.size() < StoreSize)//倉庫有空才生產 { int id = ID++; printf("生產貨物:%d\n", id); store.push(id); Sleep(arr1[id % 10] * 1000); } else{//倉庫滿了 ResetEvent(hEvent1);//把事件設為無訊號狀態 printf("倉庫滿了!\n"); } //如果倉庫有貨可以通知消費者來取貨物了 if (store.size() > 0){ SetEvent(hEvent2);//讓消費者的事件物件有訊號 } } } //消費者 void ConsumerThread(LPVOID param){ while (true) { //看event2是否允許取貨物 WaitForSingleObject(hEvent2, INFINITE); if (store.size() > 0){ int id = store.front(); store.pop();//獲取佇列中的貨物 printf("\t\t\t取出貨物:%d\n", id); Sleep(arr1[(id + 3) % 10] * 1000); } else{//倉庫空了 ResetEvent(hEvent2);//設為無訊號,不能取貨物了 printf("\t\t\t倉庫空了!\n"); } if (store.size() < 3){ SetEvent(hEvent1);//通知生產者繼續生產 } } } #endif //-------------------- C++11 多執行緒 ------------------------- #if 0//原子操作 執行緒併發,非原子操作錯誤演示 #include<iostream> #include<atomic>//原子操作標頭檔案 #include<thread> using namespace std; enum { T=100000 }; //atomic<int> N = 0; int N = 0; void ThreadFun(){ for (int i = 0; i < T/*2*T*/; ++i){ ++N;//執行緒併發導致疊加操作,不是原子操作,因此肯定少於2T } } int main(){ thread t1(ThreadFun); thread t2(ThreadFun); t1.join(); t2.join(); cout << N << endl; system("pause"); } #endif #if 0//原子操作 #include<iostream> #include<atomic>//原子操作標頭檔案 #include<thread> using namespace std; enum { T = 1000000 }; atomic<int> N = 0;//原子操作,避免多執行緒併發導致的疊加操作 //int N = 0; void ThreadFun(){ for (int i = 0; i < T/*2*T*/; ++i){ ++N; } } int main(){ thread t1(ThreadFun); thread t2(ThreadFun); t1.join(); t2.join(); cout << N << endl; system("pause"); } #endif #if 0//原子操作,十人賽跑 #include<iostream> #include<atomic> #include<thread> #include<vector> using namespace std; using namespace std::this_thread; atomic<bool> ready = false;//是否槍響 atomic_flag win = ATOMIC_FLAG_INIT;//終點線 void Run(int id){ //搶沒響不能跑 while (!ready) { yield();//讓其他執行緒先執行 } for (int i = 0; i < 100000000; ++i){} //如果沒有設定過,返回false if (!win.test_and_set()){ cout << id << " 號選手贏得第一名!" << endl; } } int main(int id){ //十個選手賽跑 vector<thread> vecPlayer; for (int i = 0; i < 10; ++i){ vecPlayer.push_back(thread(Run, i)); } cout << "十個選手已經準備好了!" << endl; //準備發命令 //sleep_for(chrono::seconds(3)); cout << "...3..." << endl; sleep_for(chrono::seconds(1)); cout << "...2..." << endl; sleep_for(chrono::seconds(1)); cout << "...1..." << endl; //可以跑了 ready = true; //等待所有選手跑完 for (thread &t : vecPlayer){ t.join(); } system("pause"); return 0; } #endif #if 0 //C++11 互斥鎖-1 #include<iostream> #include<mutex> #include<thread> #include<stdexcept> using namespace std; using namespace std::this_thread; mutex m; void ThreadFun() { try { for (int i = 0; i < 10; ++i) { sleep_for(chrono::seconds(1)); m.lock(); //lock_guard<mutex> lck(m);//哪怕發生異常也是可以安全解鎖,因為lck會析構,然後解鎖mutex if (i == 3) throw logic_error("發生異常了!");//發生異常後m無法解鎖,執行緒無法結束,所以程式會鎖死無法向下執行 cout << "A列印" << i << endl; m.unlock();//丟擲異常時,導致沒有unlock,使得B無法獲取mutex, 死鎖 } } catch (logic_error & e) { cout << "錯誤:" << e.what() << endl; } } void ThreadFun2() { for (int i = 0; i < 10; ++i) { sleep_for(chrono::seconds(1)); //lock_guard<mutex> lck(m); m.lock(); cout << "B列印" << i << endl; m.unlock(); } } int main() { thread t1(ThreadFun); thread t2(ThreadFun2); t1.join(); t2.join(); return 0; } #endif #if 0 //C++11 互斥鎖-2 #include<iostream> #include<mutex> #include<thread> #include<stdexcept> using namespace std; using namespace std::this_thread; mutex m; void ThreadFun() { try { for (int i = 0; i < 10; ++i) { sleep_for(chrono::seconds(1)); //m.lock(); lock_guard<mutex> lck(m);//哪怕發生異常也是可以安全解鎖,因為lck會析構,然後解鎖mutex if (i == 3) throw logic_error("發生異常了!"); cout << "A列印" << i << endl; //m.unlock();//丟擲異常時,導致沒有unlock,使得B無法獲取mutex, 死鎖 } } catch (logic_error & e) { cout << "錯誤" <<e.what()<< endl; } } void ThreadFun2() { for (int i = 0; i < 10; ++i) { sleep_for(chrono::seconds(1)); lock_guard<mutex> lck(m); //m.lock(); cout << "B列印" << i << endl; //m.unlock(); } } int main() { thread t1(ThreadFun); thread t2(ThreadFun2); t1.join(); t2.join(); return 0; } #endif #if 0//獨佔鎖 #include<iostream> #include<mutex> #include<thread> #include<stdexcept> using namespace std; using namespace std::this_thread; mutex m; void threadFun1(){ try{ for (int i = 0; i < 10; i++) { unique_lock<mutex> lck(m); //m.lock();// if (i == 3) throw logic_error("發生異常了!"); cout << "A 列印: " << i << endl; //m.unlock();//使用lck.lock()主動上鎖,當丟擲異常時,導致沒有unlock,使得B無法獲取mutex,死鎖 //但是使用unique_lock則不會出現死鎖的情況,離開作用於會自動解鎖,但是也可以手動lck.unlock()來手動解鎖 sleep_for(chrono::seconds(1)); } } catch (logic_error &e){ cout << "錯誤" << e.what() << endl; } } void threadFun2(){ for (int i = 0; i < 10; i++) { unique_lock<mutex> lok(m); cout << "列印B: " << i << endl; //lok.unlock();//可以手動解鎖,也可以不用 sleep_for(chrono::seconds(1)); } } int main() { thread t1(threadFun1); thread t2(threadFun2); t1.join(); t2.join(); system("pause"); return 0; } #endif #if 0 #include<iostream> #include<mutex> #include<condition_variable> #include<thread> using namespace std; using namespace std::this_thread; using namespace std::chrono; condition_variable cv;//僅支援unique_lock<mutex>作為wait的引數 condition_variable_any cv2;//接受任何lockable引數作為wait的引數 mutex m; void ThreadFun(int id){ unique_lock<mutex> lck(m); cv.wait(lck);//等待被喚醒,否則一直卡在這裡 cout << "執行緒ID: " << id << "執行!" << endl; } int main(){ thread t1(ThreadFun, 1); thread t2(ThreadFun, 2); thread t3(ThreadFun, 3); cout << "3秒後被喚醒" << endl; sleep_for(seconds(3));//3秒後被喚醒 //cv.notify_all();//喚醒所有執行緒 t1.join(); t2.join(); t3.join(); system("pause"); return 0; } #endif #if 0 //async執行緒非同步 #include<iostream> #include<future> #include<thread> using namespace std; using namespace std::this_thread; using namespace std::chrono; int work(int a, int b) { cout << "開始計算:" << endl; sleep_for(seconds(5));//假設某個計算需要耗時5s return a + b; } int main(){ future<int> result = async(work, 123, 456);//執行緒同步函式 result.wait(); //等待執行緒結束執行 cout << "算出結果:" << endl; int sum = result.get();//獲取執行緒返回的結果 cout << "最終的結果是: " << sum << endl; system("pause"); return 0; } #endif #if 0 #include<iostream> #include<future> #include<thread> using namespace std; using namespace std::this_thread; using namespace std::chrono; void work(promise<int> &prom){ cout << "開始計算!" << endl; sleep_for(seconds(3)); cout << "計算完成!" << endl; prom.set_value(666);//promise 設定結果值 可以被future get到 } int main() { //定義一個promise promise<int> prom; //future 和 promise 搭配使用,類似於aynsc future<int> result = prom.get_future(); thread t1(work, ref(prom)); t1.detach(); int sum = result.get(); cout << "獲取結果:" << sum << endl; system("pause"); return 0; } #endif