多线程学习笔记一
来源:互联网 发布:linux自定义日志级别 编辑:程序博客网 时间:2024/06/07 03:49
CreateThread, AfxBeginThread,_beginthread,_beginthreadex的区别
CreateThread是Windows的API函数(SDK函数的标准形式,直截了当的创建方式,任何场合都可以使用),提供操作系统级别的创建线程的操作,且仅限于工作者线程。不调用MFC和RTL的函数时,可以用CreateThread,其它情况不要轻易。在使用的过程中要考虑到进程的同步与互斥的关系(防止死锁)。线程函数定义为:DWORDWINAPI _yourThreadFun(LPVOID pParameter)。但它没有考虑:
(1)C Runtime中需要对多线程进行纪录和初始化,以保证C函数库工作正常(典型的例子是strtok函数)。
(2)MFC也需要知道新线程的创建,也需要做一些初始化工作(当然,如果没用MFC就没事了)。
AfxBeginThread:MFC中线程创建的MFC函数,首先创建了相应的CWinThread对象,然后调用CWinThread::CreateThread,
_beginthreadex:MS对CRuntime库的扩展SDK函数,首先针对C Runtime库做了一些初始化的工作,以保证CRuntime库工作正常。然后,调用CreateThread真正创建线程。 仅使用RuntimeLibrary时,可以用_BegingThread。
小节:实际上,这三个函数之间存在一定的调用关系,第一个纯粹一些,后两个完成自己相应的工作之后,调用前者实现线程的创建。其中CreateThread是由操作系统提供的接口,而AfxBeginThread和_BeginThread则是编译器对它的封装。
小节:用_beginthreadex()、_endthreadex函数应该是最佳选择,且都是CRun-time Library中的函数,函数的参数和数据类型都是C Run-timeLibrary中的类型,这样在启动线程时就不需要进行Windows数据类型和C Run-timeLibrary中的数据类型之间的转化,从而,减低了线程启动时的资源消耗和时间的消耗。但使用_beginthread,无法创建带有安全属性的新线程,无法创建暂停的线程,也无法获得线程ID,_endthread的情况类似,它不带参数,这意味这线程的退出代码必须硬编码为0。
小节:MFC也是C++类库(只不过是Microsoft的C++类库,不是标准的C++类库),在MFC中也封装了new和delete两中运算符,所以用到new和delete的地方不一定非要使用_beginthreadex()函数,用其他两个函数都可以。
_beginthreadex和_beginthread在回调入口函数之前进行一些线程相关的CRT的初始化操作。
CRT的函数库在线程出现之前就已经存在,所以原有的CRT不能真正支持线程,
这也导致了许多CRT的函数在多线程的情况下必须有特殊的支持,不能简单的使用CreateThread就OK。
如果用MFC编程,不要用CreateThread,如果只是使用RuntimeLibrary,用_BegingThread,总之,不要轻易使用CreateThread。这是因为在MFC和RTL中的函数有可能会用到些它们所封装的公用变量,也就是说AfxBeginThread和_BeginThread都有自己的启动代码是CreateThread所没有的。在用CreateThread所创建的线程中使用MFC的类和RTL函数就有可能出现问题。如果你是用汇编编写win32程序并且在线程函数中也不调用MFC和RTL的函数,那用CreateThread就没问题,或者你虽然是用C写线程函数,但你很小心没调用RTL函数也不会有问题。
CreateThread是由操作系统提供的接口,而AfxBeginThread和_BeginThread则是编译器对它的封装。
在可能的情况下,不要调用_beginthread,而应该调用_beginthreadex。以及对应的_endthreadex。这都是C++运行期函数。但是使用_beginthread,无法创建带有安全属性的新线程,无法创建暂停的线程,也无法获得线程ID,_endthread的情况类似,它不带参数,
这意味这线程的退出代码必须硬编码为0。这两个函数在_beginthreadex和_endthreadex中进行调用。CreateThread不要进行直接调用。
CreateThread是由操作系统提供的接口,而AfxBeginThread和_BeginThread则是编译器对它的封装。
在可能的情况下,不要调用_beginthread,而应该调用_beginthreadex。以及对应的_endthreadex。这都是C++运行期函数。但是使用_beginthread,无法创建带有安全属性的新线程,无法创建暂停的线程,也无法获得线程ID,_endthread的情况类似,它不带参数,
这意味这线程的退出代码必须硬编码为0。这两个函数在_beginthreadex和_endthreadex中进行调用。CreateThread不要进行直接调用。
1.进程与线程有那些区别和联系?
2.如何使用_beginthreadex函数?
3.如何使用CreateThread函数?
参数含义:
lpThreadAttributes
dwStackSize
lpStartAddress
lpParameter
dwCreationFlags
LpThreadId
4.如何终止线程的运行?
(1)
这是确保所有线程资源被正确地清除的唯一办法。如果线程能够返回,就可以确保下列事项的实现:
在线程函数中创建的所有C++对象均将通过它们的撤消函数正确地撤消。操作系统将正确地释放线程堆栈使用的内存。
系统将线程的退出代码设置为线程函数的返回值。系统将递减线程内核对象的使用计数。
(2)
该函数将终止线程的运行,并导致操作系统清除该线程使用的所有操作系统资源。但是,C++资源(如C++类对象)将不被撤消。
(3)
TerminateThread能撤消任何线程。线程的内核对象的使用计数也被递减。TerminateThread函数是异步运行的函数。如果要确切地知道该线程已经终止运行,必须调用WaitForSingleObject或者类似的函数。当使用返回或调用ExitThread的方法撤消线程时,该线程的内存堆栈也被撤消。但是,如果使用TerminateThread,那么在拥有线程的进程终止运行之前,系统不撤消该线程的堆栈。
(4)
5.为什么不要使用_beginthread函数和_endthread函数?
与_beginthreadex函数相比参数少,限制多。无法创建暂停的线程,无法取得线程ID。_endthread函数无参数,线程退出代码必须为0。还有_endthread函数内部关闭了线程的句柄,一旦退出将不能正确访问线程句柄。
6.如何对进程或线程的内核进行引用?
HANDLE GetCurrentProcess(
HANDLE GetCurrentThread(
这两个函数都能返回调用线程的进程的伪句柄或线程内核对象的伪句柄。伪句柄只能在当前的进程或线程中使用,在其它线程或进程将不能访问。函数并不在创建进程的句柄表中创建新句柄。调用这些函数对进程或线程内核对象的使用计数没有任何影响。如果调用CloseHandle,将伪句柄作为参数来传递,那么CloseHandle就会忽略该函数的调用并返回FALSE。
DWORD GetCurrentProcessId(
DWORD GetCurrentThreadId(
这两个函数使得线程能够查询它的进程的唯一ID或它自己的唯一ID。
7.如何将伪句柄转换为实句柄?
HANDLE hProcessFalse = NULL;
HANDLE hProcessTrue = NULL;
HANDLE hThreadFalse = NULL;
HANDLE hThreadTrue = NULL;
hProcessFalse =GetCurrentProcess(
hThreadFalse = GetCurrentThread(
取得线程实句柄:
DuplicateHandle( hProcessFalse, hThreadFalse, hProcessFalse,&hThreadTrue, 0, FALSE, DUPLICATE_SAME_ACCESS);
取得进程实句柄:
DuplicateHandle( hProcessFalse, hProcessFalse, hProcessFalse,&hProcessTrue, 0, FALSE, DUPLICATE_SAME_ACCESS);
由于DuplicateHandle会递增特定对象的使用计数,因此当完成对复制对象句柄的使用时,应该将目标句柄传递给CloseHandle,从而递减对象的使用计数。
8.在一个进程中可创建线程的最大数是得多少?
线程的最大数取决于该系统的可用虚拟内存的大小。默认每个线程最多可拥有至多1MB大小的栈的空间。所以,至多可创建2028个线程。如果减少默认堆栈的大小,则可以创建更多的线程。
线程的调度、优先级和亲缘性
9. 如何暂停和恢复线程的运行?
线程内核对象的内部有一个值指明线程的暂停计数。当调用CreateProcess或CreateThread函数时,就创建了线程的内核对象,并且它的暂停计数被初始化为1。因为线程的初始化需要时间,不能在系统做好充分的准备之前就开始执行线程。线程完全初始化好了之后,CreateProcess或CreateThread要查看是否已经传递了CREATE_SUSPENDED标志。如果已经传递了这个标志,那么这些函数就返回,同时新线程处于暂停状态。如果尚未传递该标志,那么该函数将线程的暂停计数递减为0。当线程的暂停计数是0的时候,除非线程正在等待其他某种事情的发生,否则该线程就处于可调度状态。在暂停状态中创建一个线程,就能够在线程有机会执行任何代码之前改变线程的运行环境(如优先级)。一旦改变了线程的环境,必须使线程成为可调度线程。方法如下:
hThread = CreatThread( ……,CREATE_SUSPENDED,…… );
或
bCreate = CreatProcess( ……,CREATE_SUSPENDED,……,pProcInfo );
if( bCreate != FALSE )
{
}
……
……
……
ResumeThread( hThread );
CloseHandle( hThread );
ResumeThread成功,它将返回线程的前一个暂停计数,否则返回0xFFFFFFFF。
单个线程可以暂停若干次。如果一个线程暂停了3次,它必须恢复3次。创建线程时,除了使用CREATE_SUSPENDED外,也可以调用SuspendThread函数来暂停线程的运行。任何线程都可以调用该函数来暂停另一个线程的运行(只要拥有线程的句柄)。线程可以自行暂停运行,但是不能自行恢复运行。与ResumeThread一样,SuspendThread返回的是线程的前一个暂停计数。线程暂停的最多次数可以是MAXIMUM_SUSPEND_COUNT次。SuspendThread与内核方式的执行是异步进行的,但是在线程恢复运行之前,不会发生用户方式的执行。调用SuspendThread时必须小心,因为不知道暂停线程运行时它在进行什么操作。只有确切知道目标线程是什么(或者目标线程正在做什么),并且采取强有力的措施来避免因暂停线程的运行而带来的问题或死锁状态,SuspendThread才是安全的。
10.是否可以暂停和恢复进程的运行?
11.如何使用sleep函数?
系统将在大约的指定毫秒数内使线程不可调度。Windows不是个实时操作系统。虽然线程可能在规定的时间被唤醒,但是它能否做到,取决于系统中还有什么操作正在进行。
可以调用Sleep,并且为dwMilliseconds参数传递INFINITE。这将告诉系统永远不要调度该线程。这不是一件值得去做的事情。最好是让线程退出,并还原它的堆栈和内核对象。可以将0传递给Sleep。这将告诉系统,调用线程将释放剩余的时间片,并迫使系统调度另一个线程。但是,系统可以对刚刚调用Sleep的线程重新调度。如果不存在多个拥有相同优先级的可调度线程,就会出现这种情况。
12.如何转换到另一个线程?
13.如何取得线程运行的时间?
(1)
DWORD dwStartTime = 0;
DWORD dwEndTime = 0;
DWORD dwRunTime = 0;
dwStartTime = GetTickCount(
……
……
……
dwEndTime = GetTickCount(
dwRunTime = dwEndTime – dwStartTime;
(2)
参数含义:
hThread 线程句柄
lpCreationTime 创建时间:英国格林威治时间
lpExitTime 退出时间:英国格林威治时间,如果线程仍然在运行,退出时间则未定义
lpKernelTime 内核时间:指明线程执行操作系统代码已经经过了多少个100ns的CPU时间
lpUserTime 用户时间:指明线程执行应用程序代码已经经过了多少个100ns的CPU时间
GetProcessTimes是个类似GetThreadTimes的函数,适用于进程中的所有线程(甚至是已经终止运行的线程)。返回的内核时间是所有进程的线程在内核代码中经过的全部时间的总和。GetThreadTimes和GetProcessTimes这两个函数在Windows98中不起作用。在Windows98中,没有一个可靠的机制可供应用程序来确定线程或进程已经使用了多少CPU时间。
14.进程的优先级类有哪些?
优先级类 标识符 描述
实时
高
高于正常
正常
低于正常
空闲
设置方法:
BOOL SetPriorityClass( HANDLE hProcess, DWORD dwPriority );
DWORD GetPriorityClass( HANDLE hProcess );
使用命令外壳启动一个程序时,该程序的起始优先级是正常优先级。如果使用Start命令来启动该程序,可以使用一个开关来设定应用程序的起始优先级。例如:
c:/>START /LOW CALC.EXE
Start命令还能识别/BELOWNORMAL、/NORMAL、/ABOVENORMAL、/HIGH和/REALTIME等开关。
15.线程的相对优先级有哪些?
相对优先级 标识符 描述
关键时间
最高
高于正常
正常
低于正常
最低
空闲
设置方法:
BOOL SetThreadPriority( HANDLE hThread, DWORD dwPriority );
DWORD GetThreadPriorityClass( HANDLE hThread );
16.如何避免系统动态提高线程的优先级等级?
系统常常要提高线程的优先级等级,以便对窗口消息或读取磁盘等I/O事件作出响应。或者当系统发现一个线程在大约3至4s内一直渴望得到CPU时间,它就将这个渴望得到CPU时间的线程的优先级动态提高到15,并让该线程运行两倍于它的时间量。当到了两倍时间量的时候,该线程的优先级立即返回到它的基本优先级。下面的函数可以对系统的调度方式进行设置:
BOOL SetProcessPriorityBoost( HANDLE hProcess, BOOL bDisableBoost);
BOOL GetProcessPriorityBoost( HANDLE hProcess, PBOOL pbDisableBoost);
BOOL SetThreadPriorityBoost( HANDLE hThread, BOOL bDisableBoost);
BOOL GetThreadPriorityBoost( HANDLE hThread, PBOOL pbDisableBoost);
SetProcessPriorityBoost负责告诉系统激活或停用进行中的所有线程的优先级提高功能,而SetThreadPriorityBoost则激活或停用各个线程的优先级提高功能。Windows98没有提供这4个函数的有用的实现代码。
- 多线程学习笔记(一)
- 多线程学习笔记(一)
- 多线程学习笔记 一
- 多线程学习笔记一
- 多线程学习笔记一
- Java多线程学习笔记(一)
- java多线程学习笔记(一)
- 多线程学习笔记(一)
- 多线程学习笔记(一)
- java 多线程学习笔记一
- win32多线程学习笔记(一)
- 多线程编程学习笔记(一)
- jdk5.0多线程学习笔记(一)
- VC多线程编程学习笔记(一)
- 多线程多任务学习笔记(一)
- linux多线程学习笔记一---基本概念
- Windows API 多线程-学习笔记(一)
- Posix多线程编程学习笔记(一)
- hibernate配置jndi数据源
- 基于MATLAB的OFDM系统性能仿…
- 我对OFDM的理解
- framemarker模板解析,及调后台方法 、及后台创建模板
- 调制的作用
- 多线程学习笔记一
- 大端模式与小端模式
- 为什么要进行傅立叶变换?如…
- 我的博客今天0岁47天了,我领取了…
- network architecture 网络架构
- 老婆有外遇系列之一--亲子…
- DHCP原理
- TCP/IP协议相关——IP、TCP结构及功…
- makefile