一、什么是線程?
1、線程是附屬在進程上的執行實體,是代碼的執行流程。
2、一個進程可以包含多個線程,但一個進程至少要包含一個線程
3、線程是靠CPU調度的,如果CPU沒有空閑,線程被創建也不會被執行
4、如下就是個單線程
#include "stdafx.h"
int main(int argc, char* argv[])
{
for(int i=0;i<100;i++)
{
printf("-------%d\n",i);
}
return 0;
}
二、創建線程函數
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes, // SD(安全描述符)
SIZE_T dwStackSize, // initial stack size(創建線程初始堆棧)
LPTHREAD_START_ROUTINE lpStartAddress, // thread function(要執行的代碼在哪里,就是函數)
LPVOID lpParameter, // thread argument(創建線程參數,可有可無)
DWORD dwCreationFlags, // creation option(線程標識,狀態,比如設置為0,是創建之后,立即可以被調度)
LPDWORD lpThreadId // thread identifier(線程ID)
);
三、線程函數
#include <windows.h>
//lpParameter是創建線程函數傳過來的參數
DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
}
四、多線程
圖形化界面,每創建一個功能就用多線程,不然容易造成主函數掛掉
#include "stdafx.h"
#include <windows.h>
DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
for(int i=0;i<100;i++)
{
printf("++++%d\n",i);
}
return 0;
}
int main(int argc, char* argv[])
{
HANDLE hThread;
hThread = CreateThread(NULL,0,ThreadProc,NULL,0,NULL);
for(int i=0;i<100;i++)
{
printf("-------%d\n",i);
}
return 0;
}
五、傳遞參數
1、全局變量傳參
#include "stdafx.h"
#include <windows.h>
DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
int* x = (int*)lpParameter;
for(int i=0;i<100;i++)
{
printf("++++%d\n",*x);
}
return 0;
}
int x = 2;
int main(int argc, char* argv[])
{
HANDLE hThread;
hThread = CreateThread(NULL,0,ThreadProc,&x,0,NULL);
getchar();
return 0;
}
2、局部變量傳參
傳遞的參數,需要保證比你創建的線程生命周期要長,不然就會出現傳入的參數地址被回收
六、線程ID
1、窗口和線程都不是在我們用戶層創建的,是在內核中創建的,我們沒有辦法去訪問內核,操作系統給我們一個編號(句柄),我們通過這個編號去訪問
2、線程ID是身份證,唯一的,系統進行線程調度的時候要使用的.
七、CloseHandle
CloseHandle(hThread)只是將編號不用了,不代表線程掛掉了
#include "stdafx.h"
#include <windows.h>
DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
for(int i=0;i<100;i++)
{
Sleep(500);
printf("++++%d\n",i);
}
return 0;
}
int main(int argc, char* argv[])
{
HANDLE hThread;
hThread = CreateThread(NULL,0,ThreadProc,NULL,0,NULL);
CloseHandle(hThread);
for(int i=0;i<100;i++)
{
Sleep(500);
printf("-------%d\n",i);
}
return 0;
}
八、線程掛起和恢復
演示:
1、為了演示效果明顯,創建線程后,停5秒Sleep(5000)
2、將線程掛起SuspendThread(hThread)
3、然后在停5秒Sleep(5000)
4、線程恢復ResumeThread(hThread)
5、關閉線程CloseHandle(hThread)(線程關閉,只要代碼沒執行完,內核並不會移出)
6、讓主線程main停下,getchar()(如果不停下,主線程跑完了,程序就關閉了,創建的線程就執行不了)
#include "stdafx.h"
#include <windows.h>
DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
int* p = (int*)lpParameter;
for(int i=0;i<*p;i++)
{
Sleep(500);
printf("++++%d\n",i);
}
return 0;
}
int main(int argc, char* argv[])
{
HANDLE hThread;
int n = 100;
hThread = CreateThread(NULL,0,ThreadProc,(LPVOID)&n,0,NULL);
Sleep(5000);
//掛起
SuspendThread(hThread);
Sleep(5000);
//恢復
ResumeThread(hThread);
CloseHandle(hThread);
getchar();
return 0;
}
九、線程阻塞
1、WaitForSingleObject
WaitForSingleObject
函數是用來阻塞的,只有當線程狀態發生改變或者是等待時間到了,代碼才會繼續往下執行,否則就會處於阻塞狀態
WaitForSingleObject
有兩個參數,第一個是線程的句柄,第二個是等待時間(INFINITE是一直等待,設置時間為毫秒)
#include "stdafx.h"
#include <windows.h>
DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
int* p = (int*)lpParameter;
for(int i=0;i<*p;i++)
{
Sleep(50);
printf("++++%d\n",i);
}
return 0;
}
int main(int argc, char* argv[])
{
HANDLE hThread;
int n = 100;
hThread = CreateThread(NULL,0,ThreadProc,(LPVOID)&n,0,NULL);
WaitForSingleObject(hThread,INFINITE);
printf("線程結束 ");
CloseHandle(hThread);
getchar();
return 0;
}
2、WaitForMultipleObjects
WaitForMultipleObjects
函數是用來阻塞的,只有當線程狀態發生改變或者是等待時間到了,代碼才會繼續往下執行,否則就會處於阻塞狀態
WaitForMultipleObjects
有四個參數如下
- 等幾個內核對象
- 等的內核對象句柄
- 是否等內核對象全部狀態發生變化(TRUE是等待全部內核對象狀態發生改變,FALSE是有一個就行)
- 等待時間
DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
int* p = (int*)lpParameter;
for(int i=0;i<*p;i++)
{
Sleep(50);
printf("++++%d\n",i);
}
return 0;
}
int main(int argc, char* argv[])
{
HANDLE ArrhThread[2];
int n = 100;
ArrhThread[0] = CreateThread(NULL,0,ThreadProc,(LPVOID)&n,0,NULL);
ArrhThread[1] = CreateThread(NULL,0,ThreadProc,(LPVOID)&n,0,NULL);
WaitForMultipleObjects(2,ArrhThread,TRUE,INFINITE);
printf("線程結束 ");
CloseHandle(ArrhThread[0]);
CloseHandle(ArrhThread[1]);
getchar();
return 0;
}
3、GetExitCodeThread
GetExitCodeThread
獲取線程的返回值
參數:
- 句柄
- 返回參數
#include "stdafx.h"
#include <windows.h>
DWORD WINAPI ThreadProc1(LPVOID lpParameter)
{
int* p = (int*)lpParameter;
for(int i=0;i<*p;i++)
{
Sleep(50);
printf("++++%d\n",i);
}
return 0;
}
DWORD WINAPI ThreadProc2(LPVOID lpParameter)
{
int* p = (int*)lpParameter;
for(int i=0;i<*p;i++)
{
Sleep(50);
printf("++++%d\n",i);
}
return 1;
}
int main(int argc, char* argv[])
{
HANDLE ArrhThread[2];
int n = 100;
DWORD dwResult1;
DWORD dwResult2;
ArrhThread[0] = CreateThread(NULL,0,ThreadProc1,(LPVOID)&n,0,NULL);
ArrhThread[1] = CreateThread(NULL,0,ThreadProc2,(LPVOID)&n,0,NULL);
WaitForMultipleObjects(2,ArrhThread,TRUE,INFINITE);
GetExitCodeThread(ArrhThread[0],&dwResult1);
GetExitCodeThread(ArrhThread[1],&dwResult2);
printf("線程結束\n");
CloseHandle(ArrhThread[0]);
CloseHandle(ArrhThread[1]);
printf("%d\n",dwResult1);
printf("%d\n",dwResult2);
getchar();
return 0;
}
十、單核CPU程序的切換
單核執行程序,每次切換,都會將自己的運行情況(數據),存到結構體CONTEXT context中,其中有各個寄存器的值,當程序執行完再切回來時,再將結構體中的數據還原,繼續執行
十一、鎖
1、創建全局變量
CRITICAL_SECTION cs;
2、初始化全局變量
InitializeCriticalSection(&cs);
3、 實現臨界區
EnterCriticalSection(&cs);
//使用臨界資源
LeaveCriticalSection(&cs);
#include "stdafx.h"
#include "windows.h"
int ticket = 10000;
CRITICAL_SECTION cs; //創建臨界區
DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
int* x = (int*)lpParameter;
while(ticket > 0)
{
EnterCriticalSection(&cs);//使用
if(ticket > 0)
{
printf("還有%d張票,當前線程是%d\n",ticket,*x);
ticket--;
printf("賣出一張,還有%d張,當前線程是%d\n",ticket,*x);
}
LeaveCriticalSection(&cs);//釋放
}
return 0;
}
int main(int argc, char* argv[])
{
int x =0;
int y =1;
HANDLE ArrhThread[2];
InitializeCriticalSection(&cs);//初始化臨界區
ArrhThread[0] = CreateThread(NULL,0,ThreadProc,&x,0,NULL);
ArrhThread[1] = CreateThread(NULL,0,ThreadProc,&y,0,NULL);
WaitForMultipleObjects(2,ArrhThread,TRUE,INFINITE);
printf("線程結束 ");
CloseHandle(ArrhThread[0]);
CloseHandle(ArrhThread[1]);
getchar();
return 0;
}