C++ 練習-多執行緒
阿新 • • 發佈:2019-01-02
#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