Windows进程和线程.docx
《Windows进程和线程.docx》由会员分享,可在线阅读,更多相关《Windows进程和线程.docx(19页珍藏版)》请在冰豆网上搜索。
Windows进程和线程
Windows进程和线程
1进程
1.1特点
每个进程都有自己的ID号
每个进程都有自己的地址空间,进程之间无法访问对方的地址。
每个进程都有自己的安全属性。
每个进程当中至少包含一个线程。
1.2进程环境信息
也称:
进程上下文。
1.2.1获取和释放环境信息
获取GetEnvironmentStrings
释放FreeEnvironmentStrings
1.2.2获取和设置环境变量
获取环境变量
设置环境变量
1.2.3进程的信息
1进程ID
GetCurrentProcessId
2进程句柄
GetCurrentProcess返回程序的伪句柄(-1),可以使用该句柄访问该进程的所有操作。
1.2.4进程的使用
1创建进程
WinExec-早期16位操作系统
ShellExecute-Shell操作
CreateProcess-目前最多使用
2退出线程
只能结束本进程参数没有实际意义
能结束其他及自身线程退出码没有实际意义
3通过进程ID获取进程句柄
OpenProcess
4关闭进程句柄
CloseHandle
作用:
把进程的句柄置为-1
5进程间的等候
WaitForSingleObject
等候可等候的句柄的信号。
阻塞函数,等候句柄的信号,只在句柄有信号或超出等候时间,才会结束等候。
备注
进程地址空间:
底层驱动的进程空间:
4G,驱动可任意访问上层应用的地址空间
2线程
Windows线程是可以执行的代码的实例。
系统是以线程为单位调度程序。
一个程序当中可以有多个线程,实现多任务的处理。
Windows线程的特点:
1线程都具有一个ID
2线程具有自己的安全属性
3每个线程都具有自己的栈内存(默认1M)
4每个线程都有自己的寄存器信息
进程多任务和线程多任务:
进程多任务是每个进程都使用私有地址空间,
线程多任务是进程内的多个线程使用同一个地址空间
线程的调度:
将CPU的执行时间划分成时间片,依次根据时间片执行不同的线程。
线程轮询:
线程A->线程B->线程A......
2.1线程的使用
1定义线程处理函数
2创建线程
返回线程句柄,线程句柄是可等候句柄
dwCreateionFlags方式只有两种:
0,立即执行;CREATE_SUSPENDED,使用ResumeThread函数唤醒
3结束线程
ExitThread结束本线程
TerminateThread结束制定线程
4关闭线程句柄
CloseHandle并不是关闭线程,而是关闭线程句柄
.
5线程的挂起和执行
挂起
SuspendThread
执行
ResumeThread
6线程的信息
GetCurrentThreadId-获取当前线程的ID
GetCurrentThread-获取当前线程的句柄
打开指定ID的线程,获取其句柄
OpenThread
2.2多线程的问题
线程A->线程B->线程A。
。
。
。
。
分析代码:
创建线程函数
线程处理函数
现象
分析:
当线程A执行printf输出时,如果线程A的执行时间结束,系统会将线程A的相关信息(栈、寄存器)压栈保护,同时将线程B相关信息恢复,然后执行线程B,线程B继续输出字符。
由于线程A正输出字符,线程B会继续输出,画面字符会产生混乱。
2.3线程同步技术
原子锁
临界区(段)
互斥
事件
信号量
可等候定时器
共六种线程同步技术,其中加锁的是:
原子锁、临界区(段)、互斥;剩下的三个不是加锁。
总结:
2.3.1等候函数
WaitForSingleObject-等候单个
WaitForMultipleObjects-等候多个
可等候多个句柄,但是必须是可等候句柄
bWaitAll-等候方式
TRUE-表示所有句柄都有信号,才结束等候
FASLE-表示句柄中只要有1个有信号,就结束等候。
2.4原子锁
例子:
循环一亿次,最终结果,g_nValue不是两亿
当线程A执行g_nValue1++时,如果线程切换时间
正好是在线程A将值保存到g_nValue1之前,
线程B继续执行g_nValue1++,那么当线程A再次被切换回来之后,会将原来线程A保存的值保存到g_nValue1上,
线程B进行的加法操作被覆盖。
g_nValue1++的汇编码
00401063movecx,dwordptr[g_nValue(00427c48)]
00401069addecx,1
0040106Cmovdwordptr[g_nValue(00427c48)],ecx
其伪代码如下:
ecx=g_nValue;
ecx=ecx+1;
g_nValue=ecx;
原子锁的使用
原子锁–对单条指令的操作。
InterlockedIncrement
InterlockedDecrement
InterlockedCompareExchange
InterlockedExchange
…
原子锁实现:
直接对数据所在的内存操作,并且在任何一个瞬间只能有一个线程访问。
原子锁是加锁技术中效率最高的。
2.5临界区
原子锁能实现的操作,临界区完全可以实现;临界区能实现的,原子锁不一定能实现。
相关问题
printf输出混乱,多线程情况下同时使用一段代码。
临界区可以锁定一段代码,防止多个线程同时使用该段代码
2.5.1使用
1初始化一个临界区
InitializeCriticalSection
2进入临界区
EnterCriticalSection添加到被锁定的代码之前
3离开临界区
添加到被锁定的代码之后
4删除临界区
临界区和原子锁相比,原子锁的效率比较高
2.6互斥
Mutex
相关的问题
多线程下代码或资源的共享使用。
2.6.1互斥的使用
1创建互斥
2等候互斥
WaitForSinglObject。
。
。
等待句柄填互斥句柄
3释放互斥
4关闭互斥句柄
CloseHandle
临界区和互斥的区别
临界区-用户态,执行效率高,只能在同一个进程中使用。
互斥-内核态,执行效率低,可以通过命名的方式跨进程使用。
注意:
操作系统进入win7之后,互斥已经用在内核,上层应用使用临界区
2.7事件
程序之间的通知的问题。
注意:
事件的死锁。
例如:
Thread1:
WaitFor(事件1)
SetEvent(事件2)
Thread2:
WaitFor(事件2)
SetEvent(事件1)
如此,形成死锁
2.7.1事件的使用
1创建事件CreateEvent
bManualReset事件的复位方式,TRUE手动,FALSE自动
bInitialState事件初始状态,TRUE有信号,FALSE无信号
2等候事件
WaitForSingleObject
WaitForMultipleObjects不可等待一个事件
3触发事件SetEventResetEvent
将事件设置成有信号状态
将事件设置成无信号状态
当bManualReset为FALSE时,WaitForSingleObject伪代码如下:
WaitForSingleObject
{
……
根据g_hEvent找到内存,可以获知事件复位方式
if(事件是自动复位)
{
ResetEvent…
}
……
}
4关闭事件
ColseHnadle
2.8信号量
相关的问题
类似于事件,解决通知的相关问题。
但是可以提供一个计数器,可以设置次数。
2.8.1信号量的使用
1创建信号量
CreateSemaphore
2等候信号量
WaitFor...
每等候通过一次,信号量的信号减1,直到为0阻塞
3给信号量重新指定一个计数值
ReleaseSemaphore
4关闭信号量句柄
CloseHandle
2.9可等候计时器
解决线程之间的通知问题,精度以100纳秒为单位,只适用于第一次启动时间,以后间隔事件仍然以毫秒为单位。
2.9.1可等候计时器的使用
1创建可等候定时器
CreateWaitableTimer
bManualReset复位方式
返回可等候定时器句柄
只要定时器时间没到,可等候定时器句柄无信号;可等候定时器有信号只是一瞬间。
2配置可等候定时器
SetWaitTimer
pDueTime:
第一次启动时间正数-绝对时间(年月日时分秒)
负数–相对时间(例如:
-100,000,000=10秒以后启动)
lPeriod:
间隔时间以毫秒为单位,如果参数为0,定时器只执行一次(执行启动那次)。
pfnCompletionRoutine:
APC回调函数,可以为空
lpArgToCompletionRoutine:
APC回调函数的参数
fResume:
待机处理标识TRUE:
解除待机,唤醒机器
FALSE:
放弃通知。
3等候。
。
。
WaitForSingleObject
WaitForMultipleObjects
……
4关闭信号量句柄
CloseHandle
2.9.2注意
可等候定时器只适用于NT5.0及其以上版本。
2.10练习
要求:
1按键盘数字键输出相应行数的*,比如按5,输出5行*
2按e或者按E,程序自然退出(子线程结束,主线程return)
提示:
事件和信号量配合实现
两个子线程
代码:
#include
#include
#include
HANDLEg_hEvent=0;
HANDLEg_hSem=0;
DWORDCALLBACKPrintProc(LPVOIDwParam)
{
HANDLEnHandle[2]={0};
nHandle[0]=g_hSem;
nHandle[1]=g_hEvent;
while
(1)
{
//只要有一个句柄有信号,就返回
DWORDnIndex=WaitForMultipleObjects(2,nHandle,FALSE,INFINITE);
if(nIndex==WAIT_OBJECT_0)
{
printf("********\n");
}
else
{
return0;
}
}
return0;
}
DWORDCALLBACKCtrlProc(LPVOIDwParam)
{
while
(1)
{
charcInput=getch();
if(cInput=='e'||cInput=='E')
{
//结束另外一个子线程
SetEvent(g_hEvent);
return0;
}
intnCount=cInput-'0';
if(nCount<10&&nCount>0)
{
ReleaseSemaphore(g_hSem,nCount,NULL);
}
}
return0;
}
intmain()
{
//事件
g_hEvent=CreateEvent(NULL,FALSE,FALSE,NULL);
//信号量
g_hSem=CreateSemaphore(NULL,0,10,NULL);
DWORDnID=0;
HANDLEhThread[2]={0};
hThread[0]=CreateThread(NULL,0,PrintProc,NULL,0,&nID);
hThread[1]=CreateThread(NULL,0,CtrlProc,NULL,0,&nID);
WaitForMultipleObjects(2,hThread,TRUE,INFINITE);
printf("WaitOver\n");
CloseHandle(g_hEvent);
CloseHandle(g_hSem);
CloseHandle(hThread[0]);
CloseHandle(hThread[1]);
return0;
}
3总结
1可等候句柄
进程句柄/线程句柄/事件句柄/互斥句柄/信号量/可等候计时器
WIN32到此为止~