1、CRT简介:

CRT: (C Runtime Library)即C运行时库,是系统运行的基础,包含了c常用的函数集(如:printf,malloc,strcpy等),为运行main做了初始化环境变量、堆、io等资源,并在结束后清理。

在Windows环境下,VC提供的 C run-time library又分为动态运行时库、静态运行时库、多线程、单线程、调试版本(Debug)、发行版本(Release)等。

开关

对应的库

版本

/MD

MSVCRT.LIB

多线程DLL的Release版本

/MDd

MSCVRTD.LIB

多线程DLL的Debug版本

/MT

LIBCMT.LIB

多线程静态链接的Release版本

/MTd

LIBCMTD.LIB

多线程静态链接的Debug版本

/clr

MSVCMRT.LIB

托管代码和非托管代码混合

/clr:pure

MSVCURT.LIB

纯托管代码

图1:编译器运行库设置(VS2008)

具体请参考MSDN:

ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_vccrt/html/a889fd39-807d-48f2-807f-81492612463f.htm
2、使用CRT的多线程函数集:两组

序号

函数名

功能

1

_beginthread()

创建一个新线程

2

_endthread()

结束一个线程的执行

3

_beginthreadex()

创建一个新线程

4

_endthreadex()

结束一个线程的执行

5

ResumeThread()

恢复线程的运行

6

SuspendThread()

挂起线程

7

GetExiCodeThread()

得到一个线程的退出码

8

WaitForSingleObject()

等待单个对象

9

WaitForMultipleObjects()

等待多个对象
3、两组函数的说明:

3.1、_beginthread和_endthread
该函数是C Runtime Library中的函数。其原型如下

unsigned long _beginthread(

void( __cdecl start_address )( void ),//线程函数的起始地址

unsigned stack_size,//堆栈大小,设置0为系统默认值

void *arglist );//传递给线程函数的参数,没有则为NULL

“该函数被认为是头脑简单的函数”,使用该函数导致无法有效的控制被创建线程,如不能在启动时将该线程挂起,无法为该线程设置优先权等。另外,无法利用这个Handle来等待该线程结束等操作。该函数是早期的C Runtime Library的产物,不提倡使用,后期的改良版本为_beginthreadex。
通过_beginthread启动的线程在应当通过调用_endthread结束,以保证清除与线程相关的资源。_endthread的原型为:

void _endthread(void);

3.2、_beginthreadex和_endthreadex
该函数是C Runtime Library中的一个函数,用标准C实现,相比_beginthread,_beginthreadex对线程控制更为有力(比前者多三个参数),是_beginthread的加强版。其原型为:

unsignedlong _beginthreadex(

void *security,//线程函数的安全描述符

unsigned stack_size,// 堆栈大小,设置0为系统默认值

unsigned ( __stdcall start_address )( void ),//线程函数的起始地址

void*arglist, //传递给线程函数的参数,没有则为NULL

unsignedinitflag,//初始状态,0为立即执行,CREATE_SUSPEND为创建后挂起

unsigned*thrdaddr );//指向一个32为的变量,存放线程标识符

该函数返回新线程的句柄,通过该句柄可实现对线程的控制。虽然,该函数是用标准C写的(即可不加修改就可以移植到其他系统执行),但是由于它与Windows系统有着紧密的联系(需要手动关闭该线程产生的Handle),因此实现时,往往需要包含windows.h。
通过_beginthreadex启动的线程通过调用_endthreadex做相关清理。该函数比较像CreateThread函数。

_endthreadex函数的原型为:

void _endthreadex(unsigned retVal);

关于这两组函数的详细区别请参考MSDN的说明:

Creates a thread.

uintptr_t _beginthread(

void( start_address )( void ),

unsigned stack_size,

void *arglist

);

uintptr_t _beginthreadex(

void *security,

unsigned stack_size,

unsigned ( start_address )( void ),

void *arglist,

unsigned initflag,

unsigned *thrdaddr

);
Parameters

start_address

Start address of a routine that begins execution of a new thread.For _beginthread, the calling convention is either__cdecl or __clrcall; for _beginthreadex, it iseither __stdcall or __clrcall.

stack_size

Stack size for a new thread or 0.

arglist

Argument list to be passed to a new thread or NULL.

security

Pointer to a SECURITY_ATTRIBUTES structure that determines whetherthe returned handle can be inherited by child processes. If NULL, the handlecannot be inherited. Must be NULL for Windows 95 applications.

initflag

Initial state of a new thread (0 forrunning or CREATE_SUSPENDED for suspended); useResumeThread to execute the thread.

thrdaddr

Points to a 32-bit variable that receives the thread identifier.Might be NULL, in which case it is not used.
Return Value

If successful, each of these functions returns a handle to thenewly created thread; however, if the newly created thread exits too quickly, _beginthread might not return a valid handle (see thediscussion in the Remarks section). _beginthread returns -1L on an error, in which case errno is set to EAGAIN if thereare too many threads, to EINVAL if the argument isinvalid or the stack size is incorrect, or to EACCESin the case of insufficient resources (such as memory). _beginthreadexreturns 0 on an error, in which case errno and _doserrno are set.

If startaddress is NULL, the invalid parameter handler is invoked, asdescribed in Parameter Validation. If execution is allowed to continue, these functions set errno to EINVAL and return -1.

For more information about these and other return codes, see_doserrno, errno, _sys_errlist, and _sys_nerr.

For more information about uintptr_t,see Standard Types.
Remarks

The _beginthread function creates athread that begins execution of a routine at start_address.The routine at start_address must use the __cdecl calling convention and should have no returnvalue. When the thread returns from that routine, it is terminatedautomatically. For more information about threads, see Multithreading.

_beginthreadex resembles the Win32 CreateThread API more closely than _beginthread does. _beginthreadexdiffers from _beginthread in the following ways:

· _beginthreadex has three additional parameters: initflag,security, and threadaddr.The new thread can be created in a suspended state, with a specified security(Windows NT only), and can be accessed using thrdaddr,which is the thread identifier.

· The routine at start_address passed to _beginthreadexmust use the __stdcall calling convention and mustreturn a thread exit code.

· _beginthreadex returns 0 on failure, rather than -1L.

· A thread created with _beginthreadex is terminated by a call to _endthreadex.

The _beginthreadex function gives you morecontrol over how the thread is created than _beginthreaddoes. The _endthreadex function is also moreflexible. For example, with _beginthreadex, you canuse security information, set the initial state of the thread (running orsuspended), and get the thread identifier of the newly created thread. You arealso able to use the thread handle returned by _beginthreadexwith the synchronization APIs, which you cannot do with _beginthread.

It is safer to use _beginthreadex than _beginthread. If the thread generated by _beginthread exits quickly, the handle returned to thecaller of _beginthread might be invalid or, worse,point to another thread. However, the handle returned by _beginthreadexhas to be closed by the caller of _beginthreadex, soit is guaranteed to be a valid handle if _beginthreadexdid not return an error.

You can call _endthread or _endthreadex explicitly to terminate athread; however, _endthread or _endthreadexis called automatically when the thread returns from the routine passed as aparameter. Terminating a thread with a call to endthreador _endthreadex helps to ensure proper recovery ofresources allocated for the thread.

_endthread automatically closes the thread handle (whereas _endthreadex does not). Therefore, when using _beginthread and _endthread, donot explicitly close the thread handle by calling the Win32 CloseHandle API.This behavior differs from the Win32 ExitThread API.

Note:

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.

The operating system handles the allocation of the stack wheneither _beginthread or _beginthreadexis called; you do not need to pass the address of the thread stack to either ofthese functions. In addition, the stack_sizeargument can be 0, in which case the operating system uses the same value asthe stack specified for the main thread.

arglist is a parameter to be passed to the newly created thread.Typically it is the address of a data item, such as a character string. arglist can be NULL if it isnot needed, but _beginthread and _beginthreadex must be provided with some value to pass tothe new thread. All threads are terminated if any thread calls abort, exit, _exit, or ExitProcess.

The locale of the new thread is inherited from its parent thread.If per thread locale is enabled by a call to _configthreadlocale (eitherglobally or for new threads only), the thread can change its localeindependently from its parent by calling setlocaleor _wsetlocale. For more information, see Locale.

For mixed and pure code, _beginthreadand _beginthreadex both have two overloads, onetaking a native calling-convention function pointer, the other taking a __clrcall function pointer. The first overload is notapplication domain-safe and never will be. If you are writing mixed or purecode you must ensure that the new thread enters the correct application domainbefore it accesses managed resources. You can do this, for example, by usingcall_in_appdomain Function. The second overload is application domain-safe; thenewly created thread will always end up in the application domain of the callerof _beginthread or _beginthreadex.
Requirements

Routine

Required header

_beginthread

define WM_ADD WM_USER+10//定义消息

void ThreadFunc1(void *pArg);//线程函数1:开始创建

void ThreadFunc2(void *pArg);//线程函数2:

LRESULT OnMyMsg(WPARAM, LPARAM);//消息函数

//在.cpp文件中:

//全局变量,一般在cpp中定义,不要在.h定义,否则编译出错:变量重复定义

bool g_bRun = false;

long g_nCount = 0;

//开始按钮函数

void C_beginthreadDlg::OnBnClickedButtonRun()

{

// TODO: 在此添加控件通知处理程序代码//创建线程:主要是用来创建执行创建线程池的线程if (_beginthread(ThreadFunc1,0,&m_hWnd) != -1 ){//成功GetDlgItem(IDC_BUTTON_RUN)->EnableWindow(FALSE);}

}

//创建线程池的线程函数

void ThreadFunc1(void *pArg)

{

HWND *hWnd = (HWND*)pArg;//得到窗口句柄g_nCount= 0;//初始化变量g_bRun = true;while (g_bRun){//开始创建尽可能多的线程if (_beginthread(ThreadFunc2,0,hWnd) == -1){//失败则结束g_bRun= false;break;}}//退出::SendMessage(*hWnd,WM_ADD,1,0);

}

//线程函数

void ThreadFunc2(void *pArg)

{

HWND *hWnd = (HWND*)pArg;g_nCount++;::SendMessage(*hWnd,WM_ADD,0,g_nCount);//显示个数while(g_bRun){Sleep(1000);}

}

// WM_ADD消息处理函数

LRESULT C_beginthreadDlg::OnMyMsg(WPARAM wParam, LPARAM lParam)

{

if (wParam == 1){//结束GetDlgItem(IDC_BUTTON_RUN)->EnableWindow(TRUE);}else{//显示更新m_nCount= g_nCount;UpdateData(FALSE);}return 0;

}

运行效果:

图2:实例1演示

工程源码下载地址:

http://download.csdn.net/detail/cbnotes/4902781

欢迎大家修改和指正。

注意事项:

(1)由于_beginthread()创建的线程结束后自动关闭线程句柄,所以不能使用WaitForSingleObject()/WaitForMultipleObjects()函数来同步。

(2)上面的程序中,先是创建了线程1(ThreadFunc1),再用该线程1来创建众多线程,而不是直接在OnBnClickedButtonRun()函数中创建众多线程,大家知道这是为什么吗?主要是为了能够实时显示创建的线程数目。如果直接在OnBnClickedButtonRun()函数中创建众多线程,主线程将一直处于while循环中,而不能及时处理消息,所以就不能实时显示创建的线程数目(不信的话大家可以试试)。

(3)注意线程函数参数的传递,和消息的发送。如果要传递多个参数,可以使用结构体传递。
5.2、实例2:

实例说明:该实例主要使用_beginthreadex()函数,多人合作(5人)共同完成一项任务,即平时大家说的分工合作,齐头并进。

主要函数如下:

//.h文件中

//声明线程处理函数

unsigned __stdcall ThreadFunc1( void*pArguments );//合作工作线程函数

unsigned __stdcall ThreadFunc2( void*pArguments );//检测工作完成线程函数

//为了传递多个参数,我采用结构体

struct threadInfo

{

HWND hWnd;       //窗口句柄int  nOffset;    //偏移量int  nWidth;     //宽度

};

struct threadInfo2

{

HWND   hWnd;           //窗口句柄HANDLE *phHandle;     //线程句柄指针

};

// 实现

protected:

threadInfoInfo[5];HANDLE m_hThead[5];    //用于存储线程句柄HANDLE hThead;     //用于存储线程句柄unsigned  m_dwThreadID[5];//用于存储线程的ID

threadInfo2Info2;

//在.cpp文件中

//开始按钮函数

void C_beginthreadexDlg::OnBnClickedButtonRun()

{

// TODO: 在此添加控件通知处理程序代码//使能开始按钮:无效GetDlgItem(IDC_BUTTON_RUN)->EnableWindow(FALSE);CDC *dc = GetDC();CRect rt;GetClientRect(rt);dc->FillSolidRect(0,0,rt.Width(),200,RGB(240,240,240));//刷新背景ReleaseDC(dc);int nWidth = rt.Width()/5;//初始化线程的参数Info[0].hWnd = Info[1].hWnd = Info[2].hWnd = Info[3].hWnd = Info[4].hWnd = GetSafeHwnd();Info[0].nOffset = 0;Info[1].nOffset = nWidth;Info[2].nOffset= 2*nWidth;Info[3].nOffset = 3*nWidth;Info[4].nOffset= 4*nWidth;Info[0].nWidth = Info[1].nWidth = Info[2].nWidth = Info[3].nWidth = Info[4].nWidth = nWidth;//创建个模拟线程for (int i = 0;i<5;i++){  m_hThead[i] = (HANDLE)_beginthreadex(NULL,0,ThreadFunc1,&Info[i],CREATE_SUSPENDED,&m_dwThreadID[i]); }GetDlgItem(IDC_STATIC1)->SetWindowText("进行中...");//开始运行for (int i = 0;i<5;i++){  ResumeThread(m_hThead[i]); }//开始运行监测结果线程Info2.hWnd = m_hWnd;Info2.phHandle = m_hThead;hThead =(HANDLE)_beginthreadex(NULL,0,ThreadFunc2,&Info2,0,NULL);

}

//合作工作线程函数

unsigned __stdcall ThreadFunc1( void*pArguments )

{

threadInfo*info = (threadInfo*)pArguments;CDC *dc = CWnd::FromHandle(info->hWnd)->GetDC();for (int i=info->nOffset;i<info->nOffset+info->nWidth;i++){for (int j =0 ;j<200;j++){  dc->SetPixel(i,j,RGB(0,0,0));}}DeleteObject(dc);return 0;

}

//等待所有的工作结束

unsigned __stdcall ThreadFunc2( void*pArguments )

{

threadInfo2*info = (threadInfo2*)pArguments;//等待个线程中的一个完成DWORD dwRet = WaitForMultipleObjects(5,info->phHandle,TRUE,INFINITE);if (dwRet == WAIT_FAILED){//出错啦::SendMessage(info->hWnd,WM_GAMEOVER,1,0);return 0;}//完成结束::SendMessage(info->hWnd,WM_GAMEOVER,0,0);return 0;

}

//消息处理函数

LRESULT C_beginthreadexDlg::OnGameOver(WPARAMwParam, LPARAMlParam)

{

if (wParam ==1){//出错GetDlgItem(IDC_STATIC1)->SetWindowText("出错啦!");}else{//成功//显示结果GetDlgItem(IDC_STATIC1)->SetWindowText("任务完成!");//闪动显示CDC *dc = GetDC();CRect rt;GetClientRect(rt);for (int i=0;i<8;i++){dc->Draw3dRect(0,0,rt.Width(),200,RGB(240,240,240),RGB(240,240,240));//刷新背景dc->Draw3dRect(1,1,rt.Width()-1,199,RGB(240,240,240),RGB(240,240,240));//刷新背景Sleep(100);dc->Draw3dRect(0,0,rt.Width(),200,RGB(255,0,0),RGB(255,0,0));//刷新背景dc->Draw3dRect(1,1,rt.Width()-1,199,RGB(255,0,0),RGB(255,0,0));//刷新背景Sleep(100);}ReleaseDC(dc);}//关闭句柄for (int i=0;i<5;i++){CloseHandle(m_hThead[i]);}CloseHandle(hThead);//使能开始按钮,以便可以开始下一次比赛GetDlgItem(IDC_BUTTON_RUN)->EnableWindow(TRUE);return 0;

}

运行效果:

工程源码下载地址:

http://download.csdn.net/detail/cbnotes/4905693

欢迎大家修改和指正。

注意事项:

(1)由于_beginthreadex()创建的线程结束后自动调用_endthreadex()函数,但是不会关闭线程句柄,所以必须手动关闭句柄,但能使用同步函数,如:

WaitForSingleObject()/WaitForMultipleObjects()函数来同步。

(2)通过该实例还可以稍加改进,就可以统计5个线程的各自的工作时间,并排序。欢迎大家多多练习。

(3)由_beginthreadex()函数创建线程,能灵和控制和管理线程,所以推荐使用该方式。

=================================================转载请标明出处,谢谢.http://blog.csdn.net/cbNotes

采用_beginthread/_beginthreadex函数创建多线程相关推荐

  1. python3创建多线程的几种方法

    python3创建多线程主要有下面两种方法:函数.类 1.函数创建多线程 python3中,提供了一个内置模块threading.Thread,可以很方便的创建多线程,threading.Thread ...

  2. C++多线程实例(_beginThreadex创建多线程)

    C++多线程(二)(_beginThreadex创建多线程) C/C++ Runtime 多线程函数 一 简单实例(来自codeprojct:http://www.codeproject.com/us ...

  3. _beginThreadex创建多线程解读

    _beginThreadex创建多线程解读 一.须要的头文件支持 #include <process.h>         // for _beginthread() 须要的设置:Proj ...

  4. _beginthreadex 创建多线程解读

    一.解释 (1)如果你正在编写C/C++代码,决不应该调用CreateThread.相反,应该使用VisualC++运行期库函数_beginthreadex,退出也应该使用_endthreadex.如 ...

  5. 采用CreateThread()创建多线程程序

    采用CreateThread()创建多线程程序 在window环境下,Win32 提供了一系列的API函数来完成线程的创建.挂起.恢复.终结以及通信等工作: 1.主要的函数列表: 序号 函数名 功能 ...

  6. _beginthreadex创建多线程的用法

    一.实例 #include <stdio.h> #include <string> // for STL string class #include <windows.h ...

  7. 为什么不能线程调用类的成员函数_C++多线程编程之创建线程的几种方法

    点蓝色字关注"CurryCoder的程序人生" 微信公众号:CurryCoder的程序人生 怕什么真理无穷,进一寸有一寸的欢喜 1.线程基础知识 可执行程序运行起来,就会生成一个进 ...

  8. delphi多线程例子 采用createThread创建多线程

    一.才用createThread创建多线程,需要先声明一个多线程句柄(全局的)如:hThread:THandle: 二.声明了一个多相处句柄之后,还需要先写一个函数,在创建多线程是用来掉用的函数. 三 ...

  9. 转:MFC中创建多线程

    MFC中创建多线程 MFC的多线程函数必须声明为静态的或者是全局函数(不同的在于全局函数不能访问类的私有静态成员,而静态类函数可以):但这样的线程函数只能访问静态的成员变量,要实现访问类的其他成员,可 ...

最新文章

  1. 结合实例讲解广域网路由基本技术
  2. 小米做手机是真不赚钱,米粉要支持请多容忍广告
  3. apache/nginx/tomcat的区别
  4. 为什么要用Redis
  5. JS获取浏览器信息及屏幕分辨率
  6. Python数据挖掘-词频统计-实现
  7. CentOS7中编译安装redis5.0
  8. gentoo 安装php7,在Gentoo安装Wifidog Portal
  9. 关于LINUX输入法候选框光标跟随的代码的一个网页
  10. 【软考系统架构设计师】2013年下系统架构师案例分析历年真题
  11. c语言中有队列头文件吗,C语言队列学习竟是如此简单!你,懂了嘛?
  12. C#实现秒杀器之网站登录
  13. mysql有if选择更新_使用IF条件的MySQL更新
  14. 技嘉服务器主板是什么型号,技嘉主板小型号字母代表什么意思?
  15. 超级保镖计算机管理系统
  16. Wallis公式(点火公式)
  17. WordPress给博客文章页添加个性名片
  18. redis的高级教程
  19. echarts折线图鼠标悬浮竖线_Echarts折线图问题,鼠标悬停的地方不能正确展示数据怎么回事呢?...
  20. 编写河北大学官网界面

热门文章

  1. 多元线性回归分析详细介绍
  2. 利用python实现多元线性回归
  3. 运算放大器 - 笔记 01 - 实际运用
  4. 最常用有用的英语口语900句
  5. 微信小程序 - 日期时间段选择 - picker之日期选择器
  6. 皇宫看守(guard)
  7. python打印等腰三角形,猜词小游戏,奇偶求和
  8. 全球与中国卫浴安全产品市场深度研究分析报告
  9. NOR闪存和NAND闪存的读写特点总结归纳(对比分析)
  10. 如何优雅的给SpringBoot部署的jar包瘦身?