关闭

C++多线程技术--API

标签: 多线程c++attributeswinapimfcsecurity
2357人阅读 评论(0) 收藏 举报
分类:

1. windows API相关常用的线程函数

更多详见MSDN--Process and Thread Functions

1.1. CreateThread

    CreateThread将在主线程的基础上创建一个新线程,大致做如下步骤:
    1)在内核对象中分配一个线程标识/句柄,可供管理,由CreateThread返回
    2)把线程退出码置为STILL_ACTIVE,把线程挂起计数置1
    3)分配context结构
    4)分配两页的物理存储以准备栈,保护页设置为PAGE_READWRITE,第2页设为PAGE_GUARD
    5)lpStartAddr和lpvThread值被放在栈顶,使它们成为传送给StartOfThread的参数
    6)把context结构的栈指针指向栈顶(第5步)指令指针指向startOfThread函数

HANDLE WINAPI CreateThread(
  __in_opt   LPSECURITY_ATTRIBUTES lpThreadAttributes,
  __in       SIZE_T dwStackSize,
  __in       LPTHREAD_START_ROUTINE lpStartAddress,
  __in_opt   LPVOID lpParameter,
  __in       DWORD dwCreationFlags,
  __out_opt  LPDWORD lpThreadId
);

    1)lpThreadAttributes:指向SECURITY_ATTRIBUTES型态的结构的指针。在Windows 98中忽略该参数。在Windows NT中,它被设为NULL,表示使用缺省值。
    2)dwStackSize,线程堆栈大小,一般=0,在任何情况下,Windows根据需要动态延长堆栈的大小。   
    3)lpStartAddress,指向线程函数的指针,形式:@函数名,函数名称没有限制,但是必须以下列形式声明:   DWORD WINAPI ThreadProc (LPVOID lpParam) ,格式不正确将无法调用成功。   
    4)lpParameter:向线程函数传递的参数,是一个指向结构的指针,不需传递参数时,为NULL。   
    5)dwCreationFlags :线程标志,可取值如下 :
  (a)CREATE_SUSPENDED-----创建一个挂起的线程,
  (b)0---------------------------------表示创建后立即激活。   
    6)lpThreadId:保存新线程的id。   
    7)返回值:函数成功,返回线程句柄;函数失败返回false。 
    一般并不推荐使用 CreateThread函数,而推荐使用RTL 库里的System单元中定义的 BeginThread函数,因为这除了能创建一个线程和一个入口函数以外,还增加了几项保护措施。 
    在MFC程序中,应该调用AfxBeginThread函数,在Visual C++程序中应调用_beginthreadex函数。

1.2. CreateRemoteThread 与CreateRemoteThreadEx

    创建一个在其它进程地址空间中运行的线程(也称:创建远程线程)。

HANDLE WINAPI CreateRemoteThread( 
    __in HANDLE hProcess, 
    __in LPSECURITY_ATTRIBUTES lpThreadAttributes, 
    __in SIZE_T dwStackSize, 
    __in LPTHREAD_START_ROUTINE lpStartAddress, 
    __in LPVOID lpParameter, 
    __in DWORD dwCreationFlags, 
    __out LPDWORD lpThreadId 
);
HANDLE CreateRemoteThreadEx(
  __in       HANDLE hProcess,
  __in_opt   LPSECURITY_ATTRIBUTES lpThreadAttributes,
  __in       SIZE_T dwStackSize,
  __in       LPTHREAD_START_ROUTINE lpStartAddress,
  __in_opt   LPVOID lpParameter,
  __in       DWORD dwCreationFlags,
  __in_opt   LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList,
  __out_opt  LPDWORD lpThreadId
);

    1)hProcess:线程所属进程的进程句柄. 该进程必须具有 PROCESS_CREATE_THREAD, PROCESS_QUERY_INFORMATION, PROCESS_VM_OPERATION, PROCESS_VM_WRITE,和PROCESS_VM_READ 访问权限.
    2)lpThreadAttributes:一个指向 SECURITY_ATTRIBUTES 结构的指针, 该结指定了线程的安全属性。可为NULL,表示线程拥有默认的安全属性且线程句柄不可被继承。
    3)dwStackSize:线程初始大小,以字节为单位,如果该值设为0,那么使用系统默认大小。
    4)lpStartAddress:在远程进程的地址空间中,该线程的线程函数的起始地址
    5)lpParameter:传给线程函数的参数
    6)dwCreationFlags:线程的创建标志,表示创建后立即执行线程(0),还是将线程挂起(CREATE_SUSPENDED
    7)lpThreadId:输出线程ID
    8)lpAttributeList:新线程的附加属性,通过InitializeProcThreadAttributeList创建
    9)返回值:如果执行成功,返回新线程句柄以及输出线程ID,否则返回NULL

1.3. ExitThread与TerminateThread

    用于非正常终止线程

1.3.1 ExitThread

  该函数将终止线程的运行,并导致操作系统消除该线程使用的所有操作系统资源,但是C++资源(如C++类对象堆内存)将不被撤销。可以使用ExitThread的dwExitCode参数告知系统将该线程的退出码设置为该值。

1.3.2 TerminateThread

    在线程外终止一个线程,用于强制终止线程。

BOOL TerminateThread( HANDLE hThread, DWORD dwExitCode);

    1)hThread:被终止的线程的句柄
    2)dwExitCode:设置的线程退出码
    3)返回值:函数执行成功则返回非零值,执行失败返回0。
注意:
    使用此函数时,目标线程不再有机会执行任何用户代码,线程中加载的dll不会收到线程终止的通知,由系统释放线程的初始化栈。
    TerminateThread是一个危险的函数,仅用于极端的情况。只有当你精确知道线程在做什么且在线程终止时你能控制线程将要执行的所有代码时,才可使用该函数。
    使用

1.4. SuspendThread与ResumeThread

    挂起线程和执行挂起线程

1.5. GetExitCodeThread

    获取线程退出码

 

2. 使用C运行库和Win32API

    所有 Win32 程序都至少有一个线程。任何线程都可以创建附加线程。线程可以快速完成其工作,然后终止;也可以在程序的生存期内保持活动状态。
    LIBCMT 和 MSVCRT C 运行库提供以下用于创建和终止线程的函数:_beginthread, _beginthreadex 和 _endthread, _endthreadex。
    _beginthread 和 _beginthreadex 函数创建新线程;如果操作成功,则返回线程标识符。线程完成执行时自动终止,或者可通过调用 _endthread 或 _endthreadex 自行终止。
    注意:如果要从使用 Libcmt.lib 生成的程序调用 C 运行时例程,则必须使用 _beginthread 或 _beginthreadex 函数启动线程。不要使用 Win32 函数 ExitThread 和 CreateThread。如果有一个以上的线程在等待挂起的线程完成它对 C 运行时的数据结构的访问时被阻塞,使用 SuspendThread 会导致死锁。

2.1. _beginthread 和 _beginthreadex 函数

    _beginthread 和 _beginthreadex 函数用来创建新线程。线程与进程中的其他线程共享进程的代码和数据段,但是线程具有自己的唯一寄存器值、堆栈空间和当前指令地址。系统给予每个线程 CPU 时间,使进程中的所有线程都可以同时执行。
    _beginthread 和 _beginthreadex 与 Win32 API 中的 CreateThread 函数类似,但有如下差异:
    它们初始化某些 C 运行库变量。只有在线程中使用 C 运行库时,这一点才很重要。
    CreateThread 帮助提供对安全特性的控制。可以使用此函数启动处于挂起状态的线程。
    如果成功的话,_beginthread 和 _beginthreadex 返回新线程的句柄;如果有错误的话,则返回错误代码。

2.2. _endthread 和 _endthreadex 函数

    _endthread 函数终止由 _beginthread 创建的线程(同样,_endthreadex 终止由 _beginthreadex 创建的线程)。线程会在完成时自动终止。_endthread 和 _endthreadex 用于从线程内部进行条件终止。例如,专门用于通信处理的线程若无法获取对通信端口的控制,则会退出。

3.使用C++和MFC

    MFC 应用程序中的所有线程都由 CWinThread 对象表示。大多数情况下,甚至不必显式创建这些对象,而只需调用框架 Helper 函数 AfxBeginThread,该函数将为您创建 CWinThread 对象。
    MFC区分两种类型的线程:用户界面线程和辅助线程。用户界面线程通常用于处理用户输入及响应用户生成的事件和消息。辅助线程通常用于完成不需要用户输入的任务(如重新计算)。Win32 API 不区分线程类型;它只需要了解线程的起始地址以开始执行线程。MFC 为用户界面中的事件提供消息泵,从而对用户界面线程进行专门处理。CWinApp 是用户界面线程对象的一个示例,因为它从 CWinThread 派生并对用户生成的事件和消息进行处理。

3.1. 启动线程

    AfxBeginThread 有两个重载版本:一个用于用户界面线程,一个用于辅助线程。若要开始执行辅助线程,请调用 AfxBeginThread,并提供下列信息:
    1)控制函数的地址。
    2)要传递到控制函数的参数。
    3)(可选)所需的线程优先级。默认值为正常优先级。有关可用的优先级级别的更多信息,请参见 Platform SDK 中的 SetThreadPriority。
    4)(可选)所需的线程堆栈大小。默认值与创建线程的堆栈大小相同。
    5)(可选)CREATE_SUSPENDED,如果希望在挂起状态中创建线程。默认值为 0,即正常启动线程。
    6)(可选)所需的安全属性。默认值与父线程具有相同的访问权。有关此安全信息格式的更多信息,请参见 Platform SDK 中的 SECURITY_ATTRIBUTES。
    AfxBeginThread 为您创建和初始化 CWinThread 对象、启动该对象并返回其地址,以便以后引用。在整个过程中进行检查,确保假如创建过程的任何部分出现故障,所有对象都能被正确地解除分配。

3.2. 终止线程

3.2.1 正常线程终止

    对于辅助线程,正常线程终止很简单:退出控制函数并返回表示终止原因的值。可以使用 AfxEndThread 函数或 return 语句。一般情况下,0 表示成功完成,但这取决于您自己。
    对于用户界面线程,该过程也很简单:从用户界面线程内调用 Platform SDK 中的 PostQuitMessage。PostQuitMessage 采用的唯一参数是线程的退出代码。对于辅助线程,0 通常表示成功完成。

3.2.2 过早的线程终止

    过早终止线程几乎一样简单:从线程内调用 AfxEndThread。将所需的退出代码作为唯一参数传递。这将停止执行线程、解除对线程堆栈的分配、分离附加到线程的所有 DLL 并从内存中删除线程对象。
    必须从要终止的线程内调用 AfxEndThread。如果要从其他线程终止线程,必须设置两个线程间的通信方法。

3.2.3 检索线程的退出代码

    若要获取辅助线程或用户界面线程的退出代码,请调用 GetExitCodeThread 函数。此函数获取线程(存储在 CWinThread 对象的 m_hThread 数据成员中)的句柄和 DWORD 的地址。
    如果线程仍然是活动的,GetExitCodeThread 将 STILL_ACTIVE 放置在提供的 DWORD 地址中;否则将退出代码放置在该地址中。
    检索 CWinThread 对象的退出代码还需要一步。默认情况下,当 CWinThread 线程终止时,删除该线程对象。这意味着不能访问 m_hThread 数据成员,因为 CWinThread 对象不再存在。若要避免出现这种情况,请执行以下操作之一:
    1)将 m_bAutoDelete 数据成员设置为 FALSE。这使 CWinThread 对象在线程终止后仍可以继续存在。然后可以在线程终止后,访问 m_hThread 数据成员。但是,如果使用此方法,就得销毁 CWinThread 对象,因为框架不会自动删除该对象。这是首选方法。
    2)单独存储线程的句柄。创建线程后,(使用 ::DuplicateHandle)将其 m_hThread 数据成员复制到其他变量,并通过该变量访问该成员。这样,终止后即会自动删除对象,并且仍然可以找到线程终止的原因。请注意:在可以复制句柄之前,线程不终止。执行此操作的最安全的方式是将 CREATE_SUSPENDED 传递到 AfxBeginThread,存储句柄,然后通过调用 ResumeThread 继续执行线程。
    任一方法都可以使您确定 CWinThread 对象终止的原因。

 

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:362818次
    • 积分:4825
    • 等级:
    • 排名:第6173名
    • 原创:118篇
    • 转载:18篇
    • 译文:6篇
    • 评论:76条
    博客专栏