一、什么是线程?
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;
}