Windows下的进程和Linux下的进程是不一样的,它比较懒惰,从来不执行任何东西,它只是为线程提供执行环境,然后由线程负责执行包含在进程的地址空间中的代码。当创建一个进程的时候,操作系统会自动创建这个进程的第一个线程,成为主线程。线程由两部分组成:一是线程的内核对象。操作系统用它来对线程实施管理,内核对象也是系统用来存放线程统计信息的地方。二是线程栈。线程栈用于维护线程在执行代码时所需的所有函数参数和局部变量。线程可以访问所在进程的内核对象的所有句柄、所有内存和这个进程中的其他线程的堆栈。

    在Windows下你可以调用Win SDK的API来创建一个线程,也可以用使用C Run-Time Library中的函数来创建,还可以使用MFC封装的相关线程函数来得到你想要的线程。最近正好阅读了相关方面的书籍,正好做下总结。

一  WinAPI下的线程

1 创建线程

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
);
  • lpThreadAttributes

  指向LPSECURITY_ATTRIBUTES结构体的指针,默认设为NULL,让线程使用默认的安全性。如果希望所有子线程能够继承线程对象的句柄,则必须设定LPSECURITY_ATTRIBUTES结构体,将它的bInHeriHandle初始化未TRUE。

  • dwStackSize

    设置初始栈的大小,默认设为0。

  • lpStartAddress

  指向LPTHREAD_START_ROUTINE指向的函数指针,即为新线程的起始地址。该函数的名称任意,但是函数类型必须遵照下面声明的形式:

typedef DWORD (WINAPI *PTHREAD_START_ROUTINE)(
LPVOID lpThreadParameter
);
  • lpParameter

  新线程调用函数的命令行参数。

  • dwCreationFlags

  用于控制线程创建的附加标记。可以使两个值之一:CRETE_SUSPENDED或0。如果是SUSPENDED,那么线程创建后将处于暂停状态。如果是0,线程在创建后立即运行。

  • lpThreadId

  指向一个变量,用来接受线程的ID。如果对线程ID不感兴趣可以直接设为NULL。

  注意:CreateThread()函数传回的值有两个,第一个值是返回值HANDLE,这个值是线程的HANDLE,是线程所在的进程中的局部变量,在不同的进程中是不唯一的,所以你不能直接跨进程的把一个线程的HANDLE传给另外一个进程中的线程让其使用。第二个值是由lpThreadId传回的线程ID,此值是一个全局变量,可以独一无二的表示系统中任意进程中的某个线程。比如调用AttachThreadInput()或PostThreadMessage()就需要用线程ID,而不能用线程的HANDLE。

  2  关闭线程句柄

    当完成工作后,应该带哦用CloseHandle释放核心对象。

BOOL
WINAPI
CloseHandle(
__in HANDLE hObject
);

  如果成功则传回TRUE,失败则传回FALSE。当创建一个线程后立刻调用CloseHandle()函数并不会终止新创建的线程,只是表示在主线程中新创建的线程的引用不感兴趣,因此将它关闭。当关闭该线程的句柄时,系统会递减该线程内核对象的使用计数。线程被创建的时候,线程内核对象的默认引用计数是2,当创建的这个新线程执行完毕后,系统也会递减该线程内核对象的引用计数。当使用计数为0时,系统会释放该线程内核对象。如果没有关闭线程句柄,系统就会一直保持对线程内核对象的引用,这样即使线程执行完毕,它的引用计数仍然不会为哦,这样该线程内核对象也就不会被释放,只有等到进程终止时,系统才会清理这些残留的对象。因此在程序中,当不再需要线程的句柄时,应将其关闭。

3 线程结束代码

BOOL
WINAPI
GetExitCodeThread(
__in HANDLE hThread,
__out LPDWORD lpExitCode
);

  如果成功,GetExitCodeThread()传回TRUE,否则传回FALSE。GetExitCodeThread()将传回线程函数的返回值,但是当线程还在进行尚未结束代码时,它会传回TRUE表示成功,此时lpExitCode指向的内存区域存放的是STILL_ACTIVE。

  4 结束线程

  当线程函数结束的时候,线程也就结束了,但是还可以用更暴力的手段让线程结束,就是调用线程结束函数ExitThread():

VOID
WINAPI
ExitThread(
__in DWORD dwExitCode
);

此函数可以在任何时候被调用而绝不会返回,任何在它之后的代码,都不会被调用。

还可以调用函数TerminateThread函数来杀死一个线程,不同于ExitThread总是杀死主调线程,TerminateThread可以杀死任何线程。

VOID
WINAPI
ExitThread(
HANDLE hThread,
DWORD dwExitCode
);

示例程序1:创建5个线程,此5个线程的调用函数打印出当前线程正在运行,然后返回。同时,我们另外创立一个监控监控线程,来监控这个5个线程的状态。

#include <windows.h>
#include <process.h>
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <tchar.h> #define THREAD_NUM 5 DWORD WINAPI ThreadFunc(LPVOID);
DWORD WINAPI MonitorFunc(LPVOID); void ExitFunc(void); int main( )
{
HANDLE hThread[THREAD_NUM]; for (int i = ; i < THREAD_NUM; ++i)
{
hThread[i] = CreateThread(NULL, , ThreadFunc,(LPVOID)i, , NULL);
} HANDLE monitorThread;
monitorThread = CreateThread(NULL, , MonitorFunc, (LPVOID)hThread, , NULL);
if (WaitForSingleObject(monitorThread, INFINITE))
CloseHandle(monitorThread); for (int i = ; i < THREAD_NUM; ++i)
{
if (WaitForSingleObject(hThread[i], INFINITE))
{
CloseHandle(hThread[i]);
}
} cout<<"所有线程已经运行结束!"<<endl; system("pause"); return EXIT_SUCCESS; } DWORD WINAPI ThreadFunc(LPVOID lpPara)
{
cout<<"线程"<< (int)(lpPara)<<"正在运行"<<endl;
Sleep();
return ;
} DWORD WINAPI MonitorFunc(LPVOID hThread)
{
DWORD exitCoed;
int exitThreadCount = ;
int threadStatus[THREAD_NUM];
memset(threadStatus, , THREAD_NUM); while(TRUE)
{
for (int i = ; i < THREAD_NUM; ++i)
{
GetExitCodeThread( ((HANDLE*)hThread)[i], &exitCoed);
if (exitCoed == STILL_ACTIVE)
cout<<"根据ExitCode判断:线程"<<i<<"仍然在运行!"<<endl;
else
{
cout<<"根据ExitCode判断:线程"<<i<<"已经结束运行"<<endl;
threadStatus[i] = ;
}
} exitThreadCount = ;
for (int i = ; i < THREAD_NUM; ++i)
exitThreadCount += threadStatus[i]; if (exitThreadCount == THREAD_NUM)
break;
} return ; }

二  C Run-time Library 下的线程

   在《Win32 多线程程序设计》一书中说道:“在微软的Programing Techniques说明文件中有一句看似悲惨的警告”:

  警告:如果你在一个与LIBCMT.LIB链接的程序中调用C runtime函数,你的线程就必须以_beginthread()启动之,不要使用Win32的ExitThread()和ExitThread()。这

  这句警告是说_beginthread有一个冲突状态,不可完全信赖它。所以在C Runtime Library中应该用_beiginthreadex()和_endthreadex()。主要原因是C Runtime libray是上个世纪70年代产生出来的,那个时候多任务还是新奇概念,所以更不知道多线程是什么东东了。这导致C Runtime libray中的使用的数个全局变量和静态变量可能在多线程程序中彼此引起冲突,比如errno。还有一点,_beginthread产生出来的线程所做的第一件事情就是关闭自己的handle。所以现在Visual C++提供了两个版本的C Runtime libray,一个版本供单线程程序使用,一个供多线程程序使用。第三部分介绍的MFC程序必须使用多线程的C Runtime libray,否则在链接时会出现错误。

  1 线程的创建

uintptr_t __cdecl _beginthreadex(_In_opt_ void * _Security,
_In_ unsigned _StackSize,
_In_ unsigned (__stdcall * _StartAddress) (void *),
_In_opt_ void * _ArgList,
_In_ unsigned _InitFlag,
_In_opt_ unsigned * _ThrdAddr);

  参数说明:

  • _Security 

  相当于CreateThread()中的安全属性参数,默认设置为NULL。对应Win32数据类型是LPSECURITY_ATTRIBUTES。

  • _StackSize

新线程的堆栈大小,单位是字节(byte)。对应Win32数据类型是DWORD

  • _StartAddress

  线程启动函数。

  • _ArgList

  新线程将接收到一个指针,这个指针指示单纯的被传过去,运行库并没有对它做拷贝操作。

  • _InitFlag

  启动时的状态标记。

  • _ThrdAddr

新线程的ID通过此参数传回。

_beginthreadex函数的返回值为handle,这个值必须被强制转换为Win32的HANDLE之后才能被使用。如果函数失败,则传回0,而其原因被设置在errno和doserrno全局变量中。

2 关闭线程句柄

  在C Runtime Libray下的_beiginthreadex()创建线程内部调用的就是WinAPI的CreateThread()函数。这也是很让人蛋疼的地方,微软想让C Runtime Libray跨平台,但是却使用的WinAPI的函数,想法是好的,只是没有去实现。正是这样子,所以_beiginthreadex()的返回值handle就是WinAPI中的HANDLE,只是需要强制转换下才能使用。所以,在C Runtime Libray下关闭一个线程的句柄同样是调用函数CloseHandle()。

3 结束一个线程

  在C Runtime Libray下于ExitThread()相对应的函数是_endthreadex():

 void __cdecl _endthreadex(_In_ unsigned _Retval);

  和ExitThread()函数一样,_endthreadex()可以被线程在任意时间使用,它需要表示一个“线程返回代码”的参数。但是绝对不要再一个以_beiginthreadex()启动的线程中调用ExitThrad(),因为这样C Runtime Libray就没有机会释放该线程的资源了。

  为了适当的清除C Runtime Libray中的结构,对于以_beiginthreadex()来产生新线程的程序,应该使用下面两种方法来结束程序:

  1. 调用C Runtime Libray中的exit()函数。
  2. 从main()返回系统。

  任何一种情况下,runtime libray都会自动进行清理操作,组后调用ExitProcess()。使用任一种技术都不会等待线程的结束,。

三 MFC中的线程

  如果要在MFC程序中产生一个线程,而该线程将调用MFC函数或使用MFC的任何数据,那么必须以AfxBeginThread()或CWinThread::CreateThrad()来产生这些线程。 

Windows下的多线程的更多相关文章

  1. C#Stimulator项目>>>C/C++ DLL的生成和调用,Windows下的多线程

    Windows下的多线程 http://blog.csdn.net/ganpengjin1/article/category/2541791 使用C/C++建立DLL,环境VS2013 新建Win32 ...

  2. Windows下PHP多线程扩展pthreads的安装

    pthreads扩展安装步骤 1.查看phpinfo() 获取PHP版本号及位数(x86表示32位,x64表示64位).编译器版本.PHP配置文件加载所在位置等.如下图所示: 2.pthreads扩展 ...

  3. Windows下C++多线程同步与互斥简单运用

    1.  互斥量,Mutex #include <Windows.h> #include <iostream> using namespace std; DWORD WINAPI ...

  4. Windows下C++多线程同步与互斥简单运用(转)

    1.  互斥量,Mutex #include <Windows.h> #include <iostream> using namespace std; DWORD WINAPI ...

  5. Windows下多线程编程(一)

    前言 熟练掌握Windows下的多线程编程,能够让我们编写出更规范多线程代码,避免不要的异常.Windows下的多线程编程非常复杂,但是了解一些常用的特性,已经能够满足我们普通多线程对性能及其他要求. ...

  6. Windows平台下的多线程编程

    线程是进程的一条执行路径,它包含独立的堆栈和CPU寄存器状态,每个线程共享所有的进程资源,包括打开的文件.信号标识及动态分配的内存等.一个进程内的所有线程使用同一个地址空间,而这些线程的执行由系统调度 ...

  7. 在Windows下使用Dev-C++开发基于pthread.h的多线程程序【转】

    在Windows下使用Dev-C++开发基于pthread.h的多线程程序[转]     在Windows下使用Dev-C++开发基于pthread.h的多线程程序   文章分类:C++编程     ...

  8. 原创 C++应用程序在Windows下的编译、链接:第一部分 概述

    本文是对C++应用程序在Windows下的编译.链接的深入理解和分析,文章的目录如下: 我们先看第一章概述部分. 1概述 1.1编译工具简介 cl.exe是windows平台下的编译器,link.ex ...

  9. C#实现http协议下的多线程文件传输

    用C#实现HTTP协议下的多线程文件传输转自  http://developer.51cto.com/art/201105/263066_all.htm C#(C Sharp)是微软(Microsof ...

随机推荐

  1. 排序 O(nlogn)

    1. 堆排序是一种优秀的排序算法,时间复杂度O(nlogn),主要思想是用数组构造一个最大堆,满足跟节点的value>子节点的value,然后将堆顶元素(value最大)与最后一个叶子节点交换, ...

  2. codeforce

    A. Playing with Dice time limit per test 1 second memory limit per test 256 megabytes input standard ...

  3. [转]Oracle查询树形数据的叶节点和子节点

    oracle 9i判断是叶子或根节点,是比较麻烦的一件事情,SQL演示脚本如下: --表结构-- DROP TABLE idb_hierarchical; create TABLE idb_hiera ...

  4. Eclipse SVN插件安装与使用(2014.12.27——by小赞)

    安装参考:http://www.cnblogs.com/xdp-gacl/p/3497016.html 用法参考:http://blog.sina.com.cn/s/blog_8a3d83320100 ...

  5. SRM 358(1-250,500pt)

    DIV1 250pt 题意:电视目前停留在第100台,有一个遥控器,可以向上或向下换台(需要按键一次),也可以按一些数字,然后直接跳到该台(需要按键次数等于数字数,不需要按确定键).但是,这个遥控一些 ...

  6. spark高级排序彻底解秘

    排序,真的非常重要! RDD.scala(源码) 在其,没有罗列排序,不是说它不重要! 1.基础排序算法实战 2.二次排序算法实战 3.更高级别排序算法 4.排序算法内幕解密 1.基础排序算法实战 启 ...

  7. Spring学习总结

    spring是当前流行的一个框架.在学习spring之前当然的先准备一些jar包.可以在官网找找,(也可以留言,网盘分享): 基于IDEA环境学习. 第一天完成的任务: 1.使用spring框架输出一 ...

  8. Oracle的OFA架构

    最优灵活体系结构(Optimal Flexible Architecture,简称OFA) OFA其实就是一种Oracle的一种规范,其意义就是用一种统一的给文件和文件夹的规则,和文件存放目录的规则做 ...

  9. android设备连接不上电脑的解决方法

    先检查手机usb调试是否开启,已经开启还是连不上按照以下步骤操作: 1. 打开cmd,输入adb devices  查看设备是否连接 2.服务未启动,先杀掉服务:adb kill-server 3.启 ...

  10. tar打包和解压命令

    如果你有一个大文件 想下载下来 但是又太大 可以压缩一下 然后打包 #压缩 tar -czvf ***.tar.gz tar -cjvf ***.tar.bz2 #解压缩 tar -xzvf ***. ...