Windows下的多线程
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()来产生新线程的程序,应该使用下面两种方法来结束程序:
- 调用C Runtime Libray中的exit()函数。
- 从main()返回系统。
任何一种情况下,runtime libray都会自动进行清理操作,组后调用ExitProcess()。使用任一种技术都不会等待线程的结束,。
三 MFC中的线程
如果要在MFC程序中产生一个线程,而该线程将调用MFC函数或使用MFC的任何数据,那么必须以AfxBeginThread()或CWinThread::CreateThrad()来产生这些线程。
Windows下的多线程的更多相关文章
- C#Stimulator项目>>>C/C++ DLL的生成和调用,Windows下的多线程
Windows下的多线程 http://blog.csdn.net/ganpengjin1/article/category/2541791 使用C/C++建立DLL,环境VS2013 新建Win32 ...
- Windows下PHP多线程扩展pthreads的安装
pthreads扩展安装步骤 1.查看phpinfo() 获取PHP版本号及位数(x86表示32位,x64表示64位).编译器版本.PHP配置文件加载所在位置等.如下图所示: 2.pthreads扩展 ...
- Windows下C++多线程同步与互斥简单运用
1. 互斥量,Mutex #include <Windows.h> #include <iostream> using namespace std; DWORD WINAPI ...
- Windows下C++多线程同步与互斥简单运用(转)
1. 互斥量,Mutex #include <Windows.h> #include <iostream> using namespace std; DWORD WINAPI ...
- Windows下多线程编程(一)
前言 熟练掌握Windows下的多线程编程,能够让我们编写出更规范多线程代码,避免不要的异常.Windows下的多线程编程非常复杂,但是了解一些常用的特性,已经能够满足我们普通多线程对性能及其他要求. ...
- Windows平台下的多线程编程
线程是进程的一条执行路径,它包含独立的堆栈和CPU寄存器状态,每个线程共享所有的进程资源,包括打开的文件.信号标识及动态分配的内存等.一个进程内的所有线程使用同一个地址空间,而这些线程的执行由系统调度 ...
- 在Windows下使用Dev-C++开发基于pthread.h的多线程程序【转】
在Windows下使用Dev-C++开发基于pthread.h的多线程程序[转] 在Windows下使用Dev-C++开发基于pthread.h的多线程程序 文章分类:C++编程 ...
- 原创 C++应用程序在Windows下的编译、链接:第一部分 概述
本文是对C++应用程序在Windows下的编译.链接的深入理解和分析,文章的目录如下: 我们先看第一章概述部分. 1概述 1.1编译工具简介 cl.exe是windows平台下的编译器,link.ex ...
- C#实现http协议下的多线程文件传输
用C#实现HTTP协议下的多线程文件传输转自 http://developer.51cto.com/art/201105/263066_all.htm C#(C Sharp)是微软(Microsof ...
随机推荐
- React入门1
React教程 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <t ...
- Navigation Drawer(导航抽屉)
目录(?)[-] 创建一个导航抽屉 创建抽屉布局 初始化抽屉列表 处理导航项选点击事件 监听导航抽屉打开和关闭事件 点击应用图标来打开和关闭导航抽屉 创建一个导航抽屉 导航抽屉是一个位于屏幕左侧边缘用 ...
- ASOP源码下载
vmware11下对虚拟机ubuntu14.10系统所在分区sda1进行磁盘扩容完后,重启在引导界面出现“a start job is running for dev-disk-by…”错误,产生此错 ...
- C#面向对象的三大特征
一,封装:我们可以把世界上任何一个东西都看作为一个对象,那么我们这里以人为例,一个人就肯定是一个对象了.那么封装是什么呢?封装就是这个人要完成一件事情,他所需要的任何工具都带在了自己的身上,所需要的技 ...
- 在Raspberry配置优化安装LNMP环境总结
在Raspberry配置优化安装LNMP环境总结 apt-get update apt-get install nginx apt-get install php5-fpm php5-cli php5 ...
- (转载)TRS的WCM6漏洞权限绕过以及绕过密码的登陆方式
转载于:http://www.2cto.com/Article/201302/191261.html 1.由来:建立在 TRS的WCM6可直接获取管理员密码 漏洞的基础上 2.首先访问wcm目录, ...
- Linux趣谈
要解释操作系统是如何工作的,首先可以用这几个关键字来简单地概括一下:冯诺依曼.堆栈机制和中断机制. 提到冯诺依曼很显然就是在说存储程序了,这大概是计算机能够运行最为基础的条件,属于基石级别的.它的基本 ...
- 使用 Visual Studio 生成通用的 XAML 应用程序 (Windows Phone 和 Windows 通用程序)
在Build会议上,我们发布了新的版本---Windows Phone 8.1. Windows 8.1 平台.作为开发人员,这意味着您现在可以生成 XAML 和 HTML 的通用程序,并通过分享大量 ...
- 【转】Flask快速入门
迫不及待要开始了吗?本页提供了一个很好的 Flask 介绍,并假定你已经安装好了 Flask.如果没有,请跳转到 安装 章节. 一个最小的应用 一个最小的 Flask 应用看起来会是这样: from ...
- div border-radius
可以画个1/4之一的圆也可以画整个圆 <html> <style type="text/css"> div{ background-color: #000; ...