C++进阶—>Win32 多线程的创建方法和基本使用

2023-05-16

Summary From:https://software.intel.com/zh-cn/blogs/2011/12/02/win32/?cid=sw:prccsdn2086

总结Win32提供的创建多线程相关的API接口和基本的使用框架。

Ref:

MSDN: http://msdn.microsoft.com/zh-cn/library/y6h8hye8(v=VS.100)

Win32多线程的创建方法主要有:

(1)CreateThread()

(2)_beginthread()&&_beginthreadex()

(3)AfxBeginThread()

(4)CWinThread类




(1)CreateThread()

百度百科:http://baike.baidu.com/view/1191444.htm

函数原型:

view plaincopy to clipboardprint?
01.HANDLE CreateThread(
02.  LPSECURITY_ATTRIBUTES lpThreadAttributes,
03.  DWORD dwStackSize,
04.  LPTHREAD_START_ROUTINE lpStartAddress,
05.  LPVOID lpParameter,
06.  DWORD dwCreationFlags,
07.  LPDWORD lpThreadId);
08.}
HANDLE CreateThread(
  LPSECURITY_ATTRIBUTES lpThreadAttributes,
  DWORD dwStackSize,
  LPTHREAD_START_ROUTINE lpStartAddress,
  LPVOID lpParameter,
  DWORD dwCreationFlags,
  LPDWORD lpThreadId);
}

头文件:Windows.h

CreateThread是Win32提供的创建线程的最基础的API,用于在主线程上创建一个线程。返回一个HANDLE句柄(内核对象)。

参数简要说明:

lpThreadAttributes:线程属性,用于设置线程的属性,NULL表示使用默认的设置。dwStackSize:线程堆栈大小,使用0采用默认设置,windows会根据需要动态增加堆栈大小。lpStartAddress:指向线程函数的指针。lpParameter:向线程函数传递的参数。dwCreationFlags:线程标志,CREATE_SUSPENDED表示创建一个挂起的线程,0表示创建后立即激活线程。lpThreadId,先线程的ID(输出参数)。

创建线程的代码:

view plaincopy to clipboardprint?
01.#include "stdafx.h"
02.#include
03.
04.DWORD WINAPI ThreadProc(LPVOID lpParam)
05.{
06. printf("sub thread started\n");
07. printf("sub thread finished\n");
08. return 0;
09.}
10.
11.int main(int argc, char* argv[])
12.{
13. DWORD threadID;
14. HANDLE hThread;
15. hThread = CreateThread(NULL,0,ThreadProc,NULL,0,&threadID); // 创建线程
16.
17. return 0;
18.}
#include "stdafx.h"
#include

DWORD WINAPI ThreadProc(LPVOID lpParam)
{
printf("sub thread started\n");
printf("sub thread finished\n");
return 0;
}

int main(int argc, char* argv[])
{
DWORD threadID;
HANDLE hThread;
hThread = CreateThread(NULL,0,ThreadProc,NULL,0,&threadID); // 创建线程

return 0;
}
如果直接使用上面的代码,那么很可能没有任何输出,这是由于主线程创建了子线程后主线程继续向下运行,子线程还没来得及执行里面的代码主线程可能就结束了。这就需要另一个API来进行同步:WaitForSingleObject()。

与之对应的还有WaitForMultipleObjects,用于同步一组内核对象。(参考http://msdn.microsoft.com/zh-cn/site/ms686360获取所有的同步函数(Synchronization Functions)的使用。

WaitForSingleObject原型:DWORD WINAPI WaitForSingleObject(__in HANDLE hHandle, __in DWORD dwMilliseconds);其中,第一个参数是要等待的内核对象的句柄,第二个参数是设置等待超时时间,可以设置为INFINITE,表示一直等待直到有信号触发。

在内核对象使用完毕后,一般需要关闭,使用CloseHandle()函数,参数为内核对象句柄。

所以,以下是一个最基本的使用CreateThread的例子:

view plaincopy to clipboardprint?
01.#include "stdafx.h"
02.#include
03.
04.DWORD WINAPI ThreadProc(LPVOID lpParam)
05.{
06. printf("sub thread started\n");
07. // TODO: Add your thread code here.
08. printf("sub thread finished\n");
09. return 0;
10.}
11.
12.int main(int argc, char* argv[])
13.{
14. DWORD threadID;
15. HANDLE hThread;
16. hThread = CreateThread(NULL,0,ThreadProc,NULL,0,&threadID); // 创建线程
17.
18. WaitForSingleObject(hThread,INFINITE);
19. CloseHandle(hThread); // 关闭内核对象
20.
21. return 0;
22.}
#include "stdafx.h"
#include

DWORD WINAPI ThreadProc(LPVOID lpParam)
{
printf("sub thread started\n");
// TODO: Add your thread code here.
printf("sub thread finished\n");
return 0;
}

int main(int argc, char* argv[])
{
DWORD threadID;
HANDLE hThread;
hThread = CreateThread(NULL,0,ThreadProc,NULL,0,&threadID); // 创建线程

WaitForSingleObject(hThread,INFINITE);
CloseHandle(hThread); // 关闭内核对象

return 0;

}


(2)_beginthread()&&_beginthreadex()

百度百科:http://baike.baidu.com/view/3029167.htm

MSDN:http://msdn.microsoft.com/zh-cn/library/kdzttdcb.aspx

函数原型:

view plaincopy to clipboardprint?
01.uintptr_t _beginthread( // NATIVE CODE
02. void( __cdecl *start_address )( void * ),
03. unsigned stack_size,
04. void *arglist
05.);
uintptr_t _beginthread( // NATIVE CODE
void( __cdecl *start_address )( void * ),
unsigned stack_size,
void *arglist
);

头文件:process.h

参数说明:第一个参数是线程函数的指针,第二个参数是堆栈大小,第三个参数是要传递给线程函数的参数列表。返回值也是线程句柄(关于更多说明,参考MSDN)。
同样,对于_beginthread()的同步,和CreateThread一样可以使用WaitForSingleObject函数,CloseHandle()关闭内核对象。另外,_beginthread()的线程函数是无返回值类型的,可以使用_endthread()在线程函数中结束线程。

下面是一个使用_beginthread()的基本的例子:

view plaincopy to clipboardprint?
01.#include "stdafx.h"
02.#include
03.#include
04.
05.void __cdecl ThreadProc(void *para)
06.{
07. printf("sub thread started\n");
08. // TODO: Add your thread code here.
09. printf("sub thread finished\n");
10. _endthread(); // 可以省略,隐含会调用。
11.}
12.
13.int main(int argc, char* argv[])
14.{
15. HANDLE hThread = (HANDLE)_beginthread(ThreadProc, 0, NULL);
16.
17. WaitForSingleObject(hThread,INFINITE);
18. CloseHandle(hThread);
19.}
#include "stdafx.h"
#include
#include

void __cdecl ThreadProc(void *para)
{
printf("sub thread started\n");
// TODO: Add your thread code here.
printf("sub thread finished\n");
_endthread(); // 可以省略,隐含会调用。
}

int main(int argc, char* argv[])
{
HANDLE hThread = (HANDLE)_beginthread(ThreadProc, 0, NULL);

WaitForSingleObject(hThread,INFINITE);
CloseHandle(hThread);
}另外,还有一个函数_beginthreadex(),可以简单的认为_beginthread()为其简化版,所以更多的时候是使用更简单的_beginthread()了。
说明:在MSDN中可以看到一句很重要的提示,内容为“For an executable file linked with Libcmt.lib, do not call the Win32 ExitThread API; this prevents the run-time system from reclaiming allocated resources. _endthread and _endthreadex reclaim allocated thread resources and then call ExitThread.”,简单翻译就是说,对于链接Libcmt.lib的可执行程序,不要使用Win32的线程退出函数(ExitThread),这会阻止运行时系统回收分配的资源,应该使用_endthread,它能回收分配的线程资源然后调用ExitThread。这个问题看似没有提到CreateThread(),但是其实有关,这就是经常看到有些资料上坚决的说到”不要使用CreateThread创建线程,否则会内存泄漏“的来源了。

问题引出:CreateThread的内存泄漏问题(CreateThread和_beginthread的区别)

Related Topics:http://wenku.baidu.com/view/adede4ec4afe04a1b071dea4.html http://www.cnblogs.com/whiteyun/archive/2011/06/02/2067742.html ....

1. _beginthread也是通过CreateThread来创建线程的,只是_beginthread对其进行了一些封装,将相关”资源“通过线程的本地存储(TLS)传递给了线程函数的参数,然后在调用_endthread的时候,会将这些保存的资源进行释放。

2. 并不是所有的使用CreateThread的情况都会有内存泄漏。看了很多人的文章,只有http://wenku.baidu.com/view/adede4ec4afe04a1b071dea4.html的分析是最清晰的,我已经转到http://dl.dbank.com/c03ljl2iud了,可下载查看(版权归原作者所有)。

总之,建议是使用_beginthread取代CreateThread来创建线程。

(3)AfxBeginThread():

很显然,这是MFC中的Afx系列函数,一个在MFC中创建线程的全局函数。由于现在也不怎么用MFC了,这里就不多说了。

(4)CWinThread类:

很显然,是MFC中创建线程的类,同上,不多说了。



(1)补充内容:

关于WaitForMultipleObjects在_beginthread无法使用的问题

问题:使用_beginthread创建多个线程,无法使用WaitForMultipleObjects来进行同步。

这个问题可以用下面的例子来测试:


01.#include "stdafx.h"
02.#include
03.#include
04.
05.void __cdecl ThreadProc(void *para)
06.{
07. printf("sub thread started\n");
08. // TODO: Add your thread code here.
09. printf("sub thread finished\n");
10. _endthread(); // 可以省略,隐含会调用。
11.}
12.
13.int main(int argc, char* argv[])
14.{
15. DWORD threadID;
16. HANDLE hThread[10];
17. for(int i =0;i<10;i++)
18. hThread[i] = (HANDLE)_beginthread(ThreadProc,0,NULL);
19.
20. WaitForMultipleObjects(10, hThread,TRUE,INFINITE); //无法同步所有线程!
21. for(int i = 0;i<10;i++) {
22. CloseHandle(hThread);//去掉即可 若使用closehandle会存在报错,此处
23. }
24.}

期望的结果是程序能输出10次的线程创建结束的消息,但是实际运行发现,无法达到这么多次数。为何?这是因为WaitForMultipleObjects在这里无法正常工作。原因是: _endthread()在结束线程的时候,会自动调用CloseHandle关闭内核对象。这就容易解释了,如果提前关闭了内核对象,WaitForMultipleObjects会返回错误。 (经过本人的实验(环境为vs2012 vc11),WaitForMultipleObjects函数是可以调用,可以输出10个线程。使用_beginthread创建线程,由于线程结束了会自动调用_endthread去关闭线程句柄,所以后面的CloseHandle函数是没有必要的 如果使用了会报错的 因为句柄已经被关掉了

那么关于windows api创建线程的方法_beginthread和CreateThread本人总结如下:

使用_beginthread方法需要在线程中调用_beginthread函数来释放线程(如果不手动调用会隐含的自动调用),无需调用CloseHandle关闭线程句柄,可以照常使用WaitForMultipleObjects和WaitForSingleObjects对线程进行等待,若实现同步需使用线程同步方法

使用CreateThread方法需要在创建线程后调用CloseHandle函数来关闭线程句柄,可以照常使用WaitForMultipleObjects和WaitForSingleObjects对线程进行等待,若实现同步需使用线程同步方法

若程序中使用到CRT(标准C/C++运行库)函数时,不要使用CreateThread方法创建线程,容易造成内存泄露,使用_beginthread方法创建线程,因为_beginthread方法创建的线程为CRT函数所需的进行了初始化及清理工作,在_beginthread函数中前部分是CRT函数初始准备工作 中间是调用CreateThread函数创建线程,后面是清理CRT函数初始的东西

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++进阶—>Win32 多线程的创建方法和基本使用 的相关文章

随机推荐

  • 【算法】如何判断链表有环

    如何判断单链表是否存在环 有一个单向链表 xff0c 链表当中有可能出现 环 xff0c 就像题图这样 如何用程序判断出这个链表是有环链表 xff1f 不允许修改链表结构 时间复杂度O n xff0c 空间复杂度O 1 方法一 穷举遍历 方
  • 【Android】移动端接入Cronet实践

    移动端接入Cronet实践 QUIC协议获取Chromium源码编译CronetAndroid iOS buildsDesktop builds targets the current OS Running the ninja files生
  • Linux系统下安装Java环境

    目录 测试环境 下载JDK 终端模拟软件 安装前准备 tar包的安装方法 tar包的卸载 rpm包的安装方法 rpm包的卸载 测试环境 LInux系统版本 xff1a CentOS 7 64位 终端模拟软件 xff1a Xshell 6 J
  • 【Hexo】Hexo个人博客绑定域名

    Hexo个人博客绑定域名 当我们在用hexo搭建了个人博客之后 xff0c 用username github io访问难免有些奇怪 xff0c 下面就花3分钟时间对如何绑定个人域名进行描述 我这边是在阿里云买的一个域名 xff0c ycbl
  • 生产者消费者的代码实现

    当消费者获得的数据为大写字母时 xff0c 则把大写字母转换成小写字母 xff0c 并显示 xff1b 当消费者获得的数据为小写字母时 xff0c 则把小写字母转换成大写字母 xff0c 并显示 xff1b 当消费者获得的数据为字符0 1
  • 基于RobHess的SIFT图像拼接知识点随笔

    1 SIFT算法具有尺度不变性在于构建的高斯尺度空间 xff1b 2 SIFT算法具有旋转不变性在于特征方向向量 xff1b 3 K d数以图像特征点的128维特征描述子均值为依据进行划分 构建 xff1b 4 特征点匹配是一个图像的所有特
  • 最小二乘法及OpenCv函数

    1 最小二乘法 我们以最简单的一元线性模型来解释最小二乘法 什么是一元线性模型呢 xff1f 监督学习中 xff0c 如果预测的变量是离散的 xff0c 我们称其为分类 xff08 如决策树 xff0c 支持向量机等 xff09 xff0c
  • Linux服务器网络不通情况分析以及常见检查方法

    在实际运维过程中 xff0c 经常会遇到网路不通的问题 xff0c 一般此类网络不通的问题都是业务端到端的排查 本文从后端linux服务器端自查是否服务器问题 通过多年的运维经验总结 xff0c 服务器端问题导致网络不通 xff0c 大致分
  • RANSAC算法实现去除误匹配并计算拼接矩阵-随笔

    1 RANSAC算法实现去除误匹配并计算拼接矩阵流程 1 从样本集中随机抽选一个RANSAC样本 xff0c 即4个匹配点对 xff08 至少4个匹配点对 xff0c 才能计算出3 3变换矩阵 xff09 xff1b 2 计算当错误概率为0
  • linux c++ 服务器端开发面试必看书籍

    由于很多朋友希望加入到Linux c 43 43 服务器端开发的队伍中 xff0c 本人就结合自己的面试经历并整理了自己阅读的相关书籍 xff0c 同大家分享 xff0c 一起进步 人个认为以下是进入这个方向的必看书籍 xff0c 各系列难
  • C++进阶—>const、define和enum的区别和用途

    1 区别 这三种都可以定义常量 define是宏定义 xff0c 编译器不对其进行错误检查 xff0c 在预编译阶段处理 xff0c 没有作用域限制属于全局常量 xff0c 在程序中编译器会对定义的常量名以数值进行替换 xff0c 且每次替
  • MFC中基于OpenCV实现Picture Control控件成像方法

    MFC中基于OpenCV实现Picture Control控件成像方法有两种 xff0c 一种是OpenCV2 2以前版本的绘制 xff0c 另外一种是OpenCV2 2以后版本的绘制 xff08 1 xff09 在OpenCV2 2之前的
  • MFC中CFileDialog及SHBrowseForFolder

    MFC中实现通过按钮来选择文件路径或文件夹路径 xff1b xff08 1 xff09 CFileDialog类能够选择文件 xff0c 并获取其路径 xff08 当然也可以通过获取文件路径再去除文件名而获得其所在文件夹路径 xff0c 前
  • C++进阶—>带你理解多字节编码与Unicode码

    本篇文章将讲解C 43 43 开发中容易混淆的另一个概念 多字节字符集与Unicode字符集 多字节字符与宽字节字符 char与wchar t 我们知道C 43 43 基本数据类型中表示字符的有两种 xff1a char wchar t c
  • BP神经网络及其C++实现

    0 前言 神经网络在我印象中一直比较神秘 xff0c 正好最近学习了神经网络 xff0c 特别是对Bp神经网络有了比较深入的了解 xff0c 因此 xff0c 总结以下心得 xff0c 希望对后来者有所帮助 神经网络在机器学习中应用比较广泛
  • C++进阶—>Socket通信那点事

    1 网络中进程之间如何通信 xff1f 本地的进程间通信 xff08 IPC xff09 有很多种方式 xff0c 但可以总结为下面4类 xff1a 消息传递 xff08 管道 FIFO 消息队列 xff09 同步 xff08 互斥量 条件
  • C++进阶—>线程同步随笔

    线程同步主要有五种方法 xff1a 原子访问 xff0c 临界区 xff0c 信号量 xff0c 事件和互斥量 xff1b 其中原子访问和临界区属于用户模式的同步 xff1b 信号量 xff0c 事件和互斥量属于内核模式的同步 原子访问是通
  • C++进阶—>_beginthreadex和CreateThread的区别和联系

    beginthread 和 CreateThread 的区别 转自 http wenku baidu com view adede4ec4afe04a1b071dea4 html 程序员对于Windows程序中应该用 beginthread
  • idea src下源文件和class编译文件不一致

    今天遇到一个神奇BUG xff0c 一个和elasticsearch没有任何关系的项目 xff0c 报错ES某个包找不到 xff0c 刚开始以为是依赖了父项目的某个包 xff0c 并且本项目主启动类ComponentScan扫描了相关的类进
  • C++进阶—>Win32 多线程的创建方法和基本使用

    Summary From xff1a https software intel com zh cn blogs 2011 12 02 win32 cid 61 sw prccsdn2086 总结Win32提供的创建多线程相关的API接口和基