```
// test01CreateThreadConApp210601.cpp : 此文件包含 "main" 函數。程序執行將在此處開始并結束。//
#include <iostream>
#include <windows.h>
using namespace std;
HANDLE hMutex = NULL;//互斥量
int inSub = 0;
int inParent = 0;
//線程函數
DWORD WINAPI sonFun01(LPVOID lpParamter)
{
for (int i = 0; i < 10; i++)
{
//請求一個互斥量鎖
WaitForSingleObject(hMutex, INFINITE);
++inSub;
cout << "A Thread Fun Display" << inSub<<")s "<< endl;
Sleep(100);
//釋放互斥量鎖
ReleaseMutex(hMutex);
}
return 0L;//表示返回的是long型的0
}//
int main()
{
//創建一個子線程
HANDLE hThread = CreateThread(NULL, 0, sonFun01, NULL, 0, NULL);
hMutex = CreateMutex(NULL, FALSE, L"screen");
//關閉線程
CloseHandle(hThread);
//主線程的執行路徑
Sleep(500); //parent線程先退后,讓子線程先執行
for (int i = 0; i < 10; i++)
{
//請求獲得一個互斥量鎖
WaitForSingleObject(hMutex, INFINITE);
++inParent;
cout << "Main Thread Display:" <<inParent<<"]P "<< endl;
Sleep(100);
//釋放互斥量鎖
ReleaseMutex(hMutex);
}
return 0;
}//main
```
# [Win32(CreateThread) C++多線程編程(入門實例)](https://www.cnblogs.com/codingmengmeng/p/5913068.html)
多線程在編程中有相當重要的地位,我們在實際開發時或者找工作面試時總能遇到多線程的問題,對多線程的理解程度從一個側面反映了程序員的編程水平。
其實C++語言本身并沒有提供多線程機制(當然目前C++ 11新特性中,已經可以使用std::thread來創建線程了,因為還沒有系統地了解過,所以這里不提了。),但Windows系統為我們提供了相關API,我們可以使用他們來進行多線程編程。
創建線程的API函數
[](javascript:void(0); "復制代碼")
~~~
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,//SD:線程安全相關的屬性,常置為NULL
SIZE_T dwStackSize,//initialstacksize:新線程的初始化棧的大小,可設置為0
LPTHREAD_START_ROUTINE lpStartAddress,//threadfunction:被線程執行的回調函數,也稱為線程函數
LPVOID lpParameter,//threadargument:傳入線程函數的參數,不需傳遞參數時為NULL
DWORD dwCreationFlags,//creationoption:控制線程創建的標志
LPDWORD lpThreadId//threadidentifier:傳出參數,用于獲得線程ID,如果為NULL則不返回線程ID
)
/*
lpThreadAttributes:指向SECURITY_ATTRIBUTES結構的指針,決定返回的句柄是否可被子進程繼承,如果為NULL則表示返回的句柄不能被子進程繼承。
dwStackSize:設置初始棧的大小,以字節為單位,如果為0,那么默認將使用與調用該函數的線程相同的棧空間大小。
任何情況下,Windows根據需要動態延長堆棧的大小。
lpStartAddress:指向線程函數的指針,函數名稱沒有限制,但是必須以下列形式聲明:
DWORD WINAPI 函數名 (LPVOID lpParam) ,格式不正確將無法調用成功。
lpParameter:向線程函數傳遞的參數,是一個指向結構的指針,不需傳遞參數時,為NULL。
dwCreationFlags:控制線程創建的標志,可取值如下:
(1)CREATE_SUSPENDED(0x00000004):創建一個掛起的線程(就緒狀態),直到線程被喚醒時才調用
(2)0:表示創建后立即激活。
(3)STACK_SIZE_PARAM_IS_A_RESERVATION(0x00010000):dwStackSize參數指定初始的保留堆棧的大小,
如果STACK_SIZE_PARAM_IS_A_RESERVATION標志未指定,dwStackSize將會設為系統預留的值
lpThreadId:保存新線程的id
返回值:函數成功,返回線程句柄,否則返回NULL。如果線程創建失敗,可通過GetLastError函數獲得錯誤信息。
*/
BOOL WINAPI CloseHandle(HANDLE hObject); //關閉一個被打開的對象句柄
/*可用這個函數關閉創建的線程句柄,如果函數執行成功則返回true(非0),如果失敗則返回false(0),
如果執行失敗可調用GetLastError.函數獲得錯誤信息。
*/
~~~
[](javascript:void(0); "復制代碼")
**多線程編程實例1**:
[](javascript:void(0); "復制代碼")
~~~
1 #include <iostream>
2 #include <windows.h>
3 using namespace std;
4
5 DWORD WINAPI Fun(LPVOID lpParamter)
6 {
7 for (int i = 0; i < 10; i++)
8 cout << "A Thread Fun Display!" << endl;
9 return 0L;
10 }
11
12 int main()
13 {
14 HANDLE hThread = CreateThread(NULL, 0, Fun, NULL, 0, NULL);
15 CloseHandle(hThread);
16 for (int i = 0; i < 10; i++)
17 cout << "Main Thread Display!" << endl;
18 return 0;
19 }
~~~
[](javascript:void(0); "復制代碼")
**運行結果:**

可以看到主線程(main函數)和我們自己的線程(Fun函數)是隨機交替執行的。可以看到Fun函數其實只運行了六次,這是因為主線程運行完之后將所占資源都釋放掉了,使得子線程還沒有運行完。看來主線程執行得有點快,讓它sleep一下吧。
使用函數Sleep來暫停線程的執行。
~~~
1 VOID WINAPI Sleep(
2 __in DWORD dwMilliseconds
3 );
~~~
dwMilliseconds表示千分之一秒,所以 Sleep(1000); 表示暫停1秒。
**多線程編程實例2**:
[](javascript:void(0); "復制代碼")
~~~
1 #include <iostream>
2 #include <windows.h>
3 using namespace std;
4
5 DWORD WINAPI Fun(LPVOID lpParamter)
6 {
7 for (int i = 0; i < 10; i++)
8 {
9 cout << "A Thread Fun Display!" << endl;
10 Sleep(200);
11 }
12
13 return 0L;
14 }
15
16 int main()
17 {
18 HANDLE hThread = CreateThread(NULL, 0, Fun, NULL, 0, NULL);
19 CloseHandle(hThread);
20 for (int i = 0; i < 10; i++)
21 {
22 cout << "Main Thread Display!" << endl;
23 Sleep(500);
24 }
25
26 return 0;
27 }
~~~
[](javascript:void(0); "復制代碼")
**運行結果:**

程序是每當Fun函數和main函數輸出內容后就會輸出換行,但是我們看到的確是有的時候程序輸出換行了,有的時候確沒有輸出換行,甚至有的時候是輸出兩個換行。這是怎么回事?下面我們把程序改一下看看。
**多線程編程實例3**:
[](javascript:void(0); "復制代碼")
~~~
1 #include <iostream>
2 #include <windows.h>
3 using namespace std;
4
5 DWORD WINAPI Fun(LPVOID lpParamter)
6 {
7 for (int i = 0; i < 10; i++)
8 {
9 //cout << "A Thread Fun Display!" << endl;
10 cout << "A Thread Fun Display!\n";
11 Sleep(200);
12 }
13
14 return 0L;
15 }
16
17 int main()
18 {
19 HANDLE hThread = CreateThread(NULL, 0, Fun, NULL, 0, NULL);
20 CloseHandle(hThread);
21 for (int i = 0; i < 10; i++)
22 {
23 //cout << "Main Thread Display!" << endl;
24 cout << "Main Thread Display!\n";
25 Sleep(500);
26 }
27
28 return 0;
29 }
~~~
[](javascript:void(0); "復制代碼")
**運行結果****:**

這時候,正如我們預期的,正確地輸出了我們想要輸出的內容并且格式也是正確的。在這里,我們可以把屏幕看成是一個資源,這個資源被兩個線程所共用,加入當Fun函數輸出了Fun Display!后,將要輸出endl(也就是清空緩沖區并換行,在這里我們可以不用理解什么是緩沖區),但此時,main函數卻得到了運行的機會,此時Fun函數還沒有來得及輸出換行(**時間片用完**),就把CPU讓給了main函數,而這時main函數就直接在Fun Display!后輸出Main Display!。
另一種情況就是“輸出兩個換行”,這種情況就是比如輸出Main Display!并輸出endl后,時間片用完,輪到子線程占用CPU,子進程上一次時間片用完時停在了Fun Display!,下一次時間片過來時,剛好開始輸出endl,此時就會“輸出兩個換行”。
那么為什么我們把實例2改成實例3就可以正確的運行呢?原因在于,**多個線程雖然是并發運行的,但是有一些操作(比如輸出一整段內容)是必須一氣呵成的,不允許打斷的,所以我們看到實例2和實例3的運行結果是不一樣的。它們之間的差異就是少了endl,而多了一個換行符\\n**。
那么,是不是實例2的代碼我們就不可以讓它正確的運行呢?答案當然是否定的,下面我就來講一下怎樣才能讓實例2的代碼可以正確運行。這涉及到多線程的同步問題。對于一個資源被多個線程共用會導致程序的混亂,我們的解決方法是**只允許一個線程擁有對共享資源的獨占,這里我們用互斥量(Mutex)來進行線程同步**。
在使用互斥量進行線程同步時,會用到以下幾個函數:
[](javascript:void(0); "復制代碼")
~~~
HANDLE WINAPI CreateMutex(
LPSECURITY_ATTRIBUTES lpMutexAttributes, //線程安全相關的屬性,常置為NULL
BOOL bInitialOwner, //創建Mutex時的當前線程是否擁有Mutex的所有權
LPCTSTR lpName //Mutex的名稱
);
/*
MutexAttributes:也是表示安全的結構,與CreateThread中的lpThreadAttributes功能相同,表示決定返回的句柄是否可被子進程繼承,如果為NULL則表示返回的句柄不能被子進程繼承。
bInitialOwner:表示創建Mutex時的當前線程是否擁有Mutex的所有權,若為TRUE則指定為當前的創建線程為Mutex對象的所有者,其它線程訪問需要先ReleaseMutex
lpName:Mutex的名稱
*/
~~~
[](javascript:void(0); "復制代碼")
[](javascript:void(0); "復制代碼")
~~~
DWORD WINAPI WaitForSingleObject(
HANDLE hHandle, //要獲取的鎖的句柄
DWORD dwMilliseconds //超時間隔
);
/*
WaitForSingleObject:等待一個指定的對象(如Mutex對象),直到該對象處于非占用的狀態(如Mutex對象被釋放)或超出設定的時間間隔。除此之外,還有一個與它類似的函數WaitForMultipleObjects,它的作用是等待一個或所有指定的對象,直到所有的對象處于非占用的狀態,或超出設定的時間間隔。
hHandle:要等待的指定對象的句柄。
dwMilliseconds:超時的間隔,以毫秒為單位;如果dwMilliseconds為非0,則等待直到dwMilliseconds時間間隔用完或對象變為非占用的狀態,如果dwMilliseconds 為INFINITE則表示無限等待,直到等待的對象處于非占用的狀態。
*/
~~~
[](javascript:void(0); "復制代碼")
~~~
BOOL WINAPI ReleaseMutex(HANDLE hMutex);
//說明:釋放所擁有的互斥量鎖對象,hMutex為釋放的互斥量句柄
~~~
**多線程實例4**:
[](javascript:void(0); "復制代碼")
~~~
1 #include <iostream>
2 #include <windows.h>
3 using namespace std;
4
5 HANDLE hMutex = NULL;//互斥量
6 //線程函數
7 DWORD WINAPI Fun(LPVOID lpParamter)
8 {
9 for (int i = 0; i < 10; i++)
10 {
11 //請求一個互斥量鎖
12 WaitForSingleObject(hMutex, INFINITE);
13 cout << "A Thread Fun Display!" << endl;
14 Sleep(100);
15 //釋放互斥量鎖
16 ReleaseMutex(hMutex);
17 }
18 return 0L;//表示返回的是long型的0
19
20 }
21
22 int main()
23 {
24 //創建一個子線程
25 HANDLE hThread = CreateThread(NULL, 0, Fun, NULL, 0, NULL);
26 hMutex = CreateMutex(NULL, FALSE,"screen");
27 //關閉線程
28 CloseHandle(hThread);
29 //主線程的執行路徑
30 for (int i = 0; i < 10; i++)
31 {
32 //請求獲得一個互斥量鎖
33 WaitForSingleObject(hMutex,INFINITE);
34 cout << "Main Thread Display!" << endl;
35 Sleep(100);
36 //釋放互斥量鎖
37 ReleaseMutex(hMutex);
38 }
39 return 0;
40 }
~~~
[](javascript:void(0); "復制代碼")
**運行結果:**