_beginThreadex创建多线程解读

一、需要的头文件支持

#include <process.h>         // for _beginthread()

需要的设置:ProjectàSetting-->C/C++-->User run-time library 选择Debug Multithreaded 或者Multithreaded。即使用: MT或MTD。

#include <stdio.h>
#include <string> // for STL string class
#include <windows.h> // for HANDLE
#include <process.h> // for _beginthread()
using namespace std; class ThreadX
{
private:
int loopStart;
int loopEnd;
int dispFrequency;
public:
string threadName; ThreadX( int startValue, int endValue, int frequency )
{
loopStart = startValue;
loopEnd = endValue;
dispFrequency = frequency;
} static unsigned __stdcall ThreadStaticEntryPoint(void * pThis)
{
ThreadX * pthX = (ThreadX*)pThis; // the tricky cast
pthX->ThreadEntryPoint(); // now call the true entry-point-function
return ; // the thread exit code
} void ThreadEntryPoint()
{
for (int i = loopStart; i <= loopEnd; ++i)
{
if (i % dispFrequency == )
{
printf( "%s: i = %d\n", threadName.c_str(), i );
}
}
printf( "%s thread terminating\n", threadName.c_str() );
}
}; int main()
{
ThreadX * o1 = new ThreadX( , , ); HANDLE hth1;
unsigned uiThread1ID; hth1 = (HANDLE)_beginthreadex( NULL, // security
, // stack size
ThreadX::ThreadStaticEntryPoint,
o1, // arg list
CREATE_SUSPENDED, // so we can later call ResumeThread()
&uiThread1ID ); if ( hth1 == )
printf("Failed to create thread 1\n"); DWORD dwExitCode;
GetExitCodeThread( hth1, &dwExitCode ); // should be STILL_ACTIVE = 0x00000103 = 259
printf( "initial thread 1 exit code = %u\n", dwExitCode ); o1->threadName = "t1"; ThreadX * o2 = new ThreadX( -, , ); HANDLE hth2;
unsigned uiThread2ID; hth2 = (HANDLE)_beginthreadex( NULL, // security
, // stack size
ThreadX::ThreadStaticEntryPoint,
o2, // arg list
CREATE_SUSPENDED, // so we can later call ResumeThread()
&uiThread2ID ); if ( hth2 == )
printf("Failed to create thread 2\n"); GetExitCodeThread( hth2, &dwExitCode ); // should be STILL_ACTIVE = 0x00000103 = 259
printf( "initial thread 2 exit code = %u\n", dwExitCode ); o2->threadName = "t2"; ResumeThread( hth1 ); // serves the purpose of Jaeschke's t1->Start()
ResumeThread( hth2 ); WaitForSingleObject( hth1, INFINITE );
WaitForSingleObject( hth2, INFINITE ); GetExitCodeThread( hth1, &dwExitCode );
printf( "thread 1 exited with code %u\n", dwExitCode ); GetExitCodeThread( hth2, &dwExitCode );
printf( "thread 2 exited with code %u\n", dwExitCode ); CloseHandle( hth1 );
CloseHandle( hth2 ); delete o1;
o1 = NULL; delete o2;
o2 = NULL; printf("Primary thread terminating.\n");
return ;
}

二、解释

(1)如果你正在编写C/C++代码,决不应该调用CreateThread。相反,应该使用VisualC++运行期库函数_beginthreadex,退出也应该使用_endthreadex。如果不使用Microsoft的VisualC++编译器,你的编译器供应商有它自己的CreateThread替代函数。不管这个替代函数是什么,你都必须使用。

(2)因为_beginthreadex和_endthreadex是CRT线程函数,所以必须注意编译选项runtimelibaray的选择,使用MT或MTD。[MultiThreaded , Debug MultiThreaded]。

(3)_beginthreadex函数的参数列表与CreateThread函数的参数列表是相同的,但是参数名和类型并不完全相同。这是因为Microsoft的C/C++运行期库的开发小组认为,C/C++运行期函数不应该对Windows数据类型有任何依赖。_beginthreadex函数也像CreateThread那样,返回新创建的线程的句柄。

下面是关于_beginthreadex的一些要点:

1)每个线程均获得由C/C++运行期库的堆栈分配的自己的tiddata内存结构。(tiddata结构位于Mtdll.h文件中的VisualC++源代码中)。

2)传递给_beginthreadex的线程函数的地址保存在tiddata内存块中。传递给该函数的参数也保存在该数据块中。

3)_beginthreadex确实从内部调用CreateThread,因为这是操作系统了解如何创建新线程的唯一方法。

4)当调用CreatetThread时,它被告知通过调用_threadstartex而不是pfnStartAddr来启动执行新线程。还有,传递给线程函数的参数是tiddata结构而不是pvParam的地址。

5)如果一切顺利,就会像CreateThread那样返回线程句柄。如果任何操作失败了,便返回NULL。

(4)_endthreadex的一些要点:

C运行期库的_getptd函数内部调用操作系统的TlsGetValue函数,该函数负责检索调用线程的tiddata内存块的地址。

然后该数据块被释放,而操作系统的ExitThread函数被调用,以便真正撤消该线程。当然,退出代码要正确地设置和传递。

(5)虽然也提供了简化版的的_beginthread和_endthread,但是可控制性太差,所以一般不使用。

(6)线程handle因为是内核对象,所以需要在最后closehandle。

(7)更多的API:

HANDLE GetCurrentProcess();

HANDLE GetCurrentThread();

DWORD GetCurrentProcessId();

DWORD GetCurrentThreadId()。

DWORD SetThreadIdealProcessor(HANDLE hThread,DWORDdwIdealProcessor);

BOOL SetThreadPriority(HANDLE hThread,int nPriority);

BOOL SetPriorityClass(GetCurrentProcess(),  IDLE_PRIORITY_CLASS);

BOOL GetThreadContext(HANDLE hThread,PCONTEXTpContext);

BOOL SwitchToThread();

三、注意

(1)C++主线程的终止,同时也会终止所有主线程创建的子线程,不管子线程有没有执行完毕。所以上面的代码中如果不调用WaitForSingleObject,则2个子线程t1和t2可能并没有执行完毕或根本没有执行。

(2)如果某线程挂起,然后有调用WaitForSingleObject等待该线程,就会导致死锁。所以上面的代码如果不调用resumethread,则会死锁。

四、为什么用_beginthreadex而不是CreateThread?

为什么要用C运行时库的_beginthreadex代替操作系统的CreateThread来创建线程?

来源自自1999年7月MSJ杂志的《Win32 Q&A》栏目

你也许会说我一直用CreateThread来创建线程,一直都工作得好好的,为什么要用_beginthreadex来代替CreateThread,下面让我来告诉你为什么。

回答一个问题可以有两种方式,一种是简单的,一种是复杂的。

如果你不愿意看下面的长篇大论,那我可以告诉你简单的答案:_beginthreadex在内部调用了CreateThread,在调用之前_beginthreadex做了很多的工作,从而使得它比CreateThread更安全

转载一部分,自己总结了一部分。

_beginThreadex创建多线程解读【转】的更多相关文章

  1. _beginThreadex创建多线程解读

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

  2. 使用_beginThreadex创建多线程(C语言版多线程)

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

  3. _beginthreadex创建多线程详解

    一.需要的头文件支持 #include <process.h>         // for _beginthread() 需要的设置:ProjectSetting-->C/C++- ...

  4. VC.【转】采用_beginthread/_beginthreadex函数创建多线程

    https://blog.csdn.net/cbnotes/article/details/8331632 还可以看这个网址的内容:[多线程]VC6使用_beginthread开启多线程的方法-技术宅 ...

  5. 转:MFC中创建多线程

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

  6. MFC中创建多线程

    1.   列举几种进程的同步机制,并比较其优缺点. 原子操作    信号量机制   自旋锁    管程,会合,分布式系统 2.   进程之间通信的途径 共享存储系统       消息传递系统      ...

  7. JAVA创建多线程

    首先:线程与进程的区别是什么呢? 进程:正在运行的一个程序称之为一个进程,进程负责了内存空间的划分,从宏观的角度:windows是在同时执行多个程序 从微观的角度看,CPU是在快速的切换要执行的程序. ...

  8. Java多线程开发系列之二:如何创建多线程

    前文已介绍过多线程的基本知识了,比如什么是多线程,什么又是进程,为什么要使用多线程等等. 在了解了软件开发中使用多线程的基本常识后,我们今天来聊聊如何简单的使用多线程. 在Java中创建多线程的方式有 ...

  9. java创建多线程(转载)

    转载自:Java创建线程的两个方法 Java提供了线程类Thread来创建多线程的程序.其实,创建线程与创建普通的类的对象的操作是一样的,而线程就是Thread类或其子类的实例对象.每个Thread对 ...

随机推荐

  1. PHP实现观察者模式

    <?php //php设计模式观察者模式 /** 被观察者 */ class user implements SplSubject{ public $lognum; public $hobby; ...

  2. System.currentTimeMillis()计算方式与时间的单位转换

    目录[-] 一.时间的单位转换 二.System.currentTimeMillis()计算方式 一.时间的单位转换 1秒=1000毫秒(ms) 1毫秒=1/1,000秒(s)1秒=1,000,000 ...

  3. Excel_replace

    有时候我们需要对单元格中的数据需要一些精确的处理,比如将部分以70开的工号升为706,这时简单的查找替换就不能满足我的需求,因为这样会替换掉工号中末尾或者中间位的70,造成工号的错误. 如何实现这种精 ...

  4. C#--几个数据流Stream;StreamReader;StreamWriter;MemoryStream;BufferStream;

    命名空间:System.IO; Stream: 各种流的基类,不能时行查找操作,Position属性不能修改.读取时不Position不会自动移动, HttpWebRequest webreq = ( ...

  5. Kindeditor 编辑器POST提交的时候会出现符号被转换

    Kindeditor编辑器输入符号单引号,双引号,斜杠 都会被转义 解决办法 $date['content']=$this->textString($_POST['content']); pub ...

  6. yourphp 的 ThinkTemplate.class.php与ContentReplaceBehavior.class.php

    ThinkTemplate.class.php :去掉版权(针对html代码) ContentReplaceBehavior.class.php:一些默认标签的路劲,如:__PUBLIC__,../P ...

  7. CMake安装(linux)

    环境:CentOS6.5 1.下载网址: http://www.cmake.org/download/ (选择linux版本,编译安装) 2.安装 # tar zxvf cmake-.tar.gz # ...

  8. Database Schema Reader

    数据架构与INSERT脚本生成 https://dbschemareader.codeplex.com/wikipage?title=Writing%20Data&referringTitle ...

  9. OC-SEL

    SEL SEL对应方法的地址 _cmd代表当前方法 1.  方法的存储位置 每个类的方法列表都存储在类对象中 每个方法都有一个与之对应的SEL类型的对象 根据一个SEL对象就可以找到方法的地址,进而调 ...

  10. Criteria 和 DetachedCriteria的区别与使用

    Criteria 和 DetachedCriteria 的主要区别在于创建的形式不一样, Criteria 是在线的,所以它是由 Hibernate Session 进行创建的:而 DetachedC ...