作者:佳鈺佳琴欣怡 | 来源:互联网 | 2023-10-12 13:19
前面一直讲到使用CreateThread来创建线程,那么这章告诉你应该使用_beginthreadex()取代CreateThread()。一、使用_beginthr
前面一直讲到使用CreateThread来创建线程,那么这章告诉你应该使用_beginthreadex()取代CreateThread()。
一、使用_beginthreadex()
需要的头文件支持#include // for _beginthread() 需要的设置:ProjectàSetting-->C/C++-->User run-time library 选择Debug Multithreaded 或者Multithreaded。即使用: MT或MTD。
代码如下:
#include
#include // for STL string class
#include // for HANDLE
#include // 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 &#61; startValue;loopEnd &#61; endValue;dispFrequency &#61; frequency;}static unsigned __stdcall ThreadStaticEntryPoint(void * pThis){ThreadX * pthX &#61; (ThreadX*)pThis; // the tricky castpthX->ThreadEntryPoint(); // now call the true entry-point-functionreturn 1; // the thread exit code}void ThreadEntryPoint(){for (int i &#61; loopStart; i <&#61; loopEnd; &#43;&#43;i){if (i % dispFrequency &#61;&#61; 0){printf( "%s: i &#61; %d\n", threadName.c_str(), i );}}printf( "%s thread terminating\n", threadName.c_str() );}
};int main()
{ThreadX * o1 &#61; new ThreadX( 0, 1, 2000 );HANDLE hth1;unsigned uiThread1ID;hth1 &#61; (HANDLE)_beginthreadex( NULL, // security0, // stack sizeThreadX::ThreadStaticEntryPoint,o1, // arg listCREATE_SUSPENDED, // so we can later call ResumeThread()&uiThread1ID );if ( hth1 &#61;&#61; 0 )printf("Failed to create thread 1\n");DWORD dwExitCode;GetExitCodeThread( hth1, &dwExitCode ); // should be STILL_ACTIVE &#61; 0x00000103 &#61; 259printf( "initial thread 1 exit code &#61; %u\n", dwExitCode );o1->threadName &#61; "t1";ThreadX * o2 &#61; new ThreadX( -100000, 0, 2000 );HANDLE hth2;unsigned uiThread2ID;hth2 &#61; (HANDLE)_beginthreadex( NULL, // security0, // stack sizeThreadX::ThreadStaticEntryPoint,o2, // arg listCREATE_SUSPENDED, // so we can later call ResumeThread()&uiThread2ID );if ( hth2 &#61;&#61; 0 )printf("Failed to create thread 2\n");GetExitCodeThread( hth2, &dwExitCode ); // should be STILL_ACTIVE &#61; 0x00000103 &#61; 259printf( "initial thread 2 exit code &#61; %u\n", dwExitCode );o2->threadName &#61; "t2";ResumeThread( hth1 ); // serves the purpose of Jaeschke&#39;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 &#61; NULL;delete o2;o2 &#61; NULL;printf("Primary thread terminating.\n");return 0;
}
注意&#xff1a;
&#xff08;1&#xff09;如果你正在编写C/C&#43;&#43;代码&#xff0c;决不应该调用CreateThread。相反&#xff0c;应该使用VisualC&#43;&#43;运行期库函数_beginthreadex&#xff0c;退出也应该使用_endthreadex。如果不使用Microsoft的VisualC&#43;&#43;编译器&#xff0c;你的编译器供应商有它自己的CreateThread替代函数。不管这个替代函数是什么&#xff0c;你都必须使用。
&#xff08;2&#xff09;因为_beginthreadex和_endthreadex是CRT线程函数&#xff0c;所以必须注意编译选项runtimelibaray的选择&#xff0c;使用MT或MTD。[MultiThreaded , Debug MultiThreaded]。
&#xff08;3&#xff09;_beginthreadex函数的参数列表与CreateThread函数的参数列表是相同的&#xff0c;但是参数名和类型并不完全相同。这是因为Microsoft的C/C&#43;&#43;运行期库的开发小组认为&#xff0c;C/C&#43;&#43;运行期函数不应该对Windows数据类型有任何依赖。_beginthreadex函数也像CreateThread那样&#xff0c;返回新创建的线程的句柄。
&#xff08;4&#xff09;C&#43;&#43;主线程的终止&#xff0c;同时也会终止所有主线程创建的子线程&#xff0c;不管子线程有没有执行完毕。所以上面的代码中如果不调用WaitForSingleObject&#xff0c;则2个子线程t1和t2可能并没有执行完毕或根本没有执行。
&#xff08;5&#xff09;如果某线程挂起&#xff0c;然后有调用WaitForSingleObject等待该线程&#xff0c;就会导致死锁。所以上面的代码如果不调用resumethread&#xff0c;则会死锁。
二、区别
CreateThread是Windows的API函数(SDK函数的标准形式,直截了当的创建方式&#xff0c;任何场合都可以使用)&#xff0c;提供操作系统级别的创建线程的操作&#xff0c;且仅限于工作者线程。不调用MFC和RTL的函数时&#xff0c;可以用CreateThread&#xff0c;其它情况不要轻易。在使用的过程中要考虑到进程的同步与互斥的关系&#xff08;防止死锁&#xff09;。线程函数定义为&#xff1a;DWORD WINAPI _yourThreadFun(LPVOID pParameter)。但它没有考虑&#xff1a;
&#xff08;1&#xff09;C Runtime中需要对多线程进行纪录和初始化&#xff0c;以保证C函数库工作正常&#xff08;典型的例子是strtok函数&#xff09;。
&#xff08;2&#xff09;MFC也需要知道新线程的创建&#xff0c;也需要做一些初始化工作&#xff08;当然&#xff0c;如果没用MFC就没事了&#xff09;。
AfxBeginThread&#xff1a;MFC中线程创建的MFC函数&#xff0c;首先创建了相应的CWinThread对象&#xff0c;然后调用CWinThread::CreateThread, 在CWinThread::CreateThread中&#xff0c;完成了对线程对象的初始化工作&#xff0c;然后&#xff0c;调用_beginthreadex(AfxBeginThread相比较更为安全)创建线程。它简化了操作或让线程能够响应消息&#xff0c;即可用于界面线程&#xff0c;也可以用于工作者线程&#xff0c;但要注意不要在一个MFC程序中使用_beginthreadex()或CreateThread()。线程函数定义为&#xff1a;UINT _yourThreadFun(LPVOID pParam)
_beginthreadex&#xff1a;MS对C Runtime库的扩展SDK函数&#xff0c;首先针对C Runtime库做了一些初始化的工作&#xff0c;以保证C Runtime库工作正常。然后&#xff0c;调用CreateThread真正创建线程。 仅使用Runtime Library时&#xff0c;可以用_BegingThread。
小节&#xff1a;实际上&#xff0c;这三个函数之间存在一定的调用关系&#xff0c;第一个纯粹一些&#xff0c;后两个完成自己相应的工作之后&#xff0c;调用前者实现线程的创建。其中CreateThread是由操作系统提供的接口&#xff0c;而AfxBeginThread和_BeginThread则是编译器对它的封装。
小节&#xff1a;用_beginthreadex()、_endthreadex函数应该是最佳选择&#xff0c;且都是C Run-time Library中的函数&#xff0c;函数的参数和数据类型都是C Run-time Library中的类型&#xff0c;这样在启动线程时就不需要进行Windows数据类型和C Run-time Library中的数据类型之间的转化&#xff0c;从而&#xff0c;减低了线程启动时的资源消耗和时间的消耗。但使用_beginthread&#xff0c;无法创建带有安全属性的新线程&#xff0c;无法创建暂停的线程&#xff0c;也无法获得 线程ID&#xff0c;_endthread的情况类似&#xff0c;它不带参数&#xff0c;这意味这线程的退出代码必须硬编码为0。
小节&#xff1a;MFC也是C&#43;&#43;类库&#xff08;只不过是Microsoft的C&#43;&#43;类库&#xff0c;不是标准的C&#43;&#43;类库&#xff09;&#xff0c;在MFC中也封装了new和delete两中运算符&#xff0c;所以用到new和delete的地方不一定非要使用_beginthreadex() 函数&#xff0c;用其他两个函数都可以。
_beginthreadex和_beginthread在回调入口函数之前进行一些线程相关的CRT的初始化操作。
CRT的函数库在线程出现之前就已经存在&#xff0c;所以原有的CRT不能真正支持线程&#xff0c;
这也导致了许多CRT的函数在多线程的情况下必须有特殊的支持&#xff0c;不能简单的使用CreateThread就OK。
补充一点&#xff0c;_beginthreadex()是针对CRT的线程函数&#xff0c;在线程中若要用到CRT的函数&#xff0c;最好用这个启动线程&#xff0c;如果不用这个会有内存泄漏。