多线程与同步对象文档格式.docx
- 文档编号:16353002
- 上传时间:2022-11-23
- 格式:DOCX
- 页数:23
- 大小:24.21KB
多线程与同步对象文档格式.docx
《多线程与同步对象文档格式.docx》由会员分享,可在线阅读,更多相关《多线程与同步对象文档格式.docx(23页珍藏版)》请在冰豆网上搜索。
如果希望所有的子进程能够继承该线程对象的句柄,须设定一个SECURITY_ATTRIBUTES结构,它的bInheritHandle成员应初始化为TRUE。
DWORDdwStackSize。
该参数指定线程栈的大小。
每个线程都拥有它自己的堆栈。
如果dwStakSize为0,系统默认保留的栈的空间为1MB。
LPTHREAD_START_ROUTINElpStartAddress。
该参数用于指定新建线程的入口函数的地址。
可以创建多个线程,使用相同的入口函数地址。
该入口函数的原型为:
DWORDWINAPIThreadFunc(LPVOIDlpParameter);
其中的lpParameter的值为创建线程时,CreateThread函数的第4个参数的值。
LPVOIDlpParameter。
参见3、由CreateThread传给入口线程入口函数的参数。
DWORDdwCreationFlags。
该参数控制创建线程的标志,它可以是0或者CREATE_SUSPENDED。
如果是0,则新建立的线程在创建完毕后被系统调度程序调度(可能被执行,也可能不被执行,这取决于系统中其他线程的优先级情况)。
如果该值为CREATE_SUSPENDED,系统在创建完新线程后,新线程被系统挂起,直到有其他线程执行了带该新线程句柄的ResumeThread()函数后,新线程才被激活。
LPDWORDlpThreadId。
该参数指定新线程的ID。
在Windows95中,该参数不能为NULL,否则会引起错误,在Windows2000中该参数可以为NULL。
例1:
创建线程,并传递参数:
DWORDWINAPISubThread(LPVOIDlpParam)
{
TRACE("
SubThread,lpParamis:
%d\n"
lpParam);
return0;
}
voidMainThread()
HANDLEhThread=CreateThread(NULL,0,SubThread,(LPVOID)123,0,NULL);
Sleep(1000);
CloseHandle(hThread);
例2:
创建挂起的新线程,老线程执行一些工作后,再激活新创建的线程。
DWORDWINAPISubThread(LPVOIDlpParam)
HANDLEhThread=CreateThread(NULL,0,SubThread,(LPVOID)123,CREATE_SUSPENDED,NULL);
SubThreadiscreated\n"
);
ResumeThread(hThread);
1.2.线程的终止
可以使用下面方法来终止线程:
●线程函数的返回;
●通过调用ExitThread函数,线程将自己撤销;
●同一个进程或者另一个进程中的线程调用TerminateThread函数终止另外一个线程的运行;
●包含线程的进程终止运行(主线程退出)。
1、线程函数返回。
这是确保线程的所有资源被正确地清除的唯一办法。
当线程函数返回时,如下情况将会发生:
●在线程函数中创建的所有C++对象将通过它们的析构函数正确地撤销;
●操作系统将正确地释放线程的堆栈使用的内存;
●系统将线程的退出代码设置为线程函数的返回值;
●系统递减线程内核对象的引用计数。
2、ExitThread函数。
可以通过在线程中调用ExitThread函数,来强制终止自身线程的运行。
原型为:
VOIDExitThread(DWORDdwExitCode);
该函数将终止自身线程的运行,并导致操作系统清除该线程使用的所有操作系统资源。
但是,C++资源(如C++对象)将不被正确地撤销。
由于这个原因,最好从线程函数返回,而不是通过调用ExitThread来返回。
3、TerminateThread函数。
调用TerminateThread函数将终止指定线程的运行,原型为:
BOOLTerminateThread(HANDLEhThread,DWORDdwExitCode);
//HANDLEhThread—将要终止的线程的句柄
//DWORDdwExitCode—传递给将要终止的线程的退出代码
与ExitThread不同,ExitThread是撤销自身线程,而TerminateThread能够撤销任何线程。
要注意的是,TerminateThread是异步运行的函数,也就是说,它告诉系统要终止指定线程的运行,但是该函数返回后,并不能保证指定的线程已经撤销,如果要确切地知道被指定的线程是否已经被撤销,请调用
WaitForSingleObject等函数。
4、进程终止运行(主线程退出)
5、线程终止运行时,会发生下列操作:
●线程拥有的所有USER对象句柄均被释放。
在Windows中,线程所创建的大部分对象归它的进程所有。
但是,线程也可以拥有两个USER对象:
窗口和钩子。
当创建这些对象的线程终止运行时,系统会自动释放这些对象。
其它对对象只有在进程终止时才被释放;
●线程的退出代码从STILL_ACTIVE改为线程函数返回值或者传递给ExitThread或者TerminateThread的代码;
●线程内核对象的状态变为通知(信号)状态;
●如果线程是进程中最后一个活动线程,进程也被终止;
●线程内核对象的使用技术递减1。
线程的正常退出。
注意!
在线程中new出来的资源并不能随着线程退出而自动释放!
!
classCTest
private:
intm_iId;
public:
CTest(intiId)
{
m_iId=iId;
}
virtual~CTest()
ID:
%d~CTest()\n"
m_iId);
};
CTestobj
(1),*pObj;
pObj=newCTest
(2);
//注意,堆分配的资源并不随着线程的退出而自动释放
Sleep(500);
return999;
DWORDdwRet;
HANDLEhThread=CreateThread(NULL,0,SubThread,NULL,0,NULL);
GetExitCodeThread(hThread,&
dwRet);
TRACE("
SubThreadexitcode:
dwRet);
//很可能为STILL_ACTIVE
//很可能为999
CloseHandle(hThread);
例2:
自身调用ExitThread退出线程
classCTest
ExitThread(1000);
//接下来的返回语句将得不到执行,obj也不会被析构
//很可能为1000
例3:
调用TerminateThread来终止另外一个线程的运行。
CTestobj
(1),*pObj;
DWORDdwRet;
//很可能为STILL_ACTIVE
TerminateThread(hThread,1001);
//强行终止线程hThread的运行
//TerminateThread是异步函数,所以可能此时hThread并没撤销完毕。
GetExitCodeThread(hThread,&
//很可能为1001
1.3.线程的暂停(挂起)与恢复运行
任何线程都可以调用SuspendThread来暂停另一个线程的运行(只要拥有线程的句柄)。
DWORDSuspendThread(HANDLEhThread);
返回值是前一次暂停计数,一个线程能够被暂停的最多次数是MAXIMUM_SUSPEND_COUNT,
参数HANDLEhThread表示将要被挂起的线程
调用ResumeThread可以让挂起的线程恢复运行。
DWORDResumeThread(HANDLEhThread);
返回值是前一次暂停计数,参数HANDLEhThread表示将要被恢复的线程
例:
HANDLEg_hThread=NULL;
intiLoop=0;
while(TRUE)
SubThreadloop:
iLoop++);
Sleep(1000);
voidCMy0621Dlg:
:
OnOK()
g_hThread=CreateThread(NULL,0,SubThread,NULL,0,NULL);
OnButtonSuspend()
DWORDdwCount=SuspendThread(g_hThread);
Suspendcount:
dwCount);
OnButtonResume()
DWORDdwCount=ResumeThread(g_hThread);
1.4.线程的优先级
HANDLEg_hThread1=NULL;
HANDLEg_hThread2=NULL;
voidConsumeCPU()
charszBuf[8192];
for(inti=0;
i<
200000;
i++)
sprintf(szBuf,"
%d"
rand());
memset(szBuf,0,sizeof(szBuf));
ConsumeCPU();
Thread%Xloop:
GetCurrentThreadId(),iLoop++);
g_hThread1=CreateThread(NULL,0,SubThread,NULL,0,NULL);
g_hThread2=CreateThread(NULL,0,SubThread,NULL,0,NULL);
SetThreadPriority(g_hThread1,THREAD_PRIORITY_LOWEST);
SetThreadPriority(g_hThread2,THREAD_PRIORITY_LOWEST);
OnButtonReduce()
SetThreadPriority(g_hThread1,THREAD_PRIORITY_IDLE);
OnButtonRestore()
1.5.C运行时的线程创建
C运行时是在Windows操作系统尚未面世时就已经存在的一套C语言的函数库,因为当时并未考虑到多线程的情况,所以在Windows操作系统下用CreateThread创建的线程中调用了某些C运行时函数,如asctime等,则有可能出现问题,为此后来特地增加了CreateThread的C运行时版本_beginthreadex,原型为:
unsignedint_beginthreadex(
void*security,
unsignedstack_size,
unsigned(__stdcall*start_address)(void*),
void*arglist,
unsignedinitflag,
unsigned*thrdaddr);
相应的线程入口点函数也要变为:
UINTWINAPISubThread(LPVOIDlpParameter);
UINTWINAPISubThread(LPVOIDlpParameter)
lpParameteris:
lpParameter);
HANDLEhThread=(HANDLE)_beginthreadex(NULL,0,SubThread,(LPVOID)123,0,NULL);
ExitCodeis:
2.多线程的同步
当多个线程无限制的在同一段时间内访问同一资源时,有可能导致错误的结果的发生,例:
longg_iNum1,g_iNum2;
i<
100000000;
i++)
g_iNum1++;
g_iNum2=g_iNum2+2;
voidTest()
HANDLEhThreads[2];
g_iNum1=0;
g_iNum2=0;
hThreads[0]=CreateThread(NULL,0,SubThread,NULL,0,NULL);
hThreads[1]=CreateThread(NULL,0,SubThread,NULL,0,NULL);
SetThreadPriority(hThreads[0],THREAD_PRIORITY_LOWEST);
SetThreadPriority(hThreads[1],THREAD_PRIORITY_LOWEST);
WaitForMultipleObjects(2,hThreads,TRUE,INFINITE);
g_iNum1:
%dg_iNum2:
g_iNum1,g_iNum2);
最终TRACE的输出将很可能不是200000000和400000000
为解决此类问题,必须引入同步处理机制。
常用的同步处理机制包括互锁函数、临界区、和进程、线程、互斥量、信号量、事件等Windows内核对象。
2.1.原子访问,互锁函数
互锁函数提供了一套多个线程同步访问一个简单变量的处理机制。
●LONGInterlockedIncrement(LONGvolatile*lpAddend);
该函数提供多线程情况下,对一个变量以原子操作方式增加1
●LONGInterlockedDecrement(LONGvolatile*lpAddend);
该函数提供多线程情况下,对一个变量以原子操作方式减少1
●LONGInterlockedExchange(LONGvolatile*lpTarget,LONGlValue);
该函数提供在多线程情况下,以原子操作方式用lValue给lpTarget指向的目标变量赋值,并返回赋值以前的lpTarget指向的值。
●LONGInterlockedExchangeAdd(LONGvolatile*lpAddend,LONGlValue)
该函数提供在多线程情况下,以原子的操作方式将lpAddend指向的变量增加lValue,并返回调用前的lpAddend指向的目标变量的值。
示例:
InterlockedIncrement(&
g_iNum1);
InterlockedExchangeAdd(&
g_iNum2,2);
return0;
2.2.临界区
临界区是一段连续的代码区域,它要求在执行前获得对某些共享数据的独占的访问权。
如果一个进程中的所有线程中访问这些共享数据的代码都放在临界区中,就能够实现对该共享数据的同步访问。
临界区只能用于同步单个进程中的线程。
//多个线程共享的全局数据
//实例化临界区对象
CRITICAL_SECTIONg_sec;
//进入临界区,临界区对象的引用计数加1,同一个线程可以多次调用
//EnterCriticalSection,但是如果调用n次EnterCriticalSection以后,
//必须再调用n次的LeaveCriticalSection,使临界区对象的引用计数变为0,
//其它的线程才能进入临界区
EnterCriticalSection(&
g_sec);
g_iNum2+=2;
//离开临界区
LeaveCriticalSection(&
voidTest()
//初始化临界区对象
InitializeCriticalSection(&
hT
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 多线程 同步 对象