C++多线程(二)(_beginThreadex创建多线程)

C/C++ Runtime 多线程函数

一 简单实例(来自codeprojct:http://www.codeproject.com/useritems/MultithreadingTutorial.asp)
主线程创建2个线程t1和t2,创建时2个线程就被挂起,后来调用ResumeThread恢复2个线程,是其开始执行,调用WaitForSingleObject等待2个线程执行完,然后推出主线程即结束进程。

/*  file Main.cpp
 *
 *  This program is an adaptation of the code Rex Jaeschke showed in
 *  Listing 1 of his Oct 2005 C/C++ User's Journal article entitled
 *  "C++/CLI Threading: Part I".  I changed it from C++/CLI (managed)
 *  code to standard C++.
 *
 *  One hassle is the fact that C++ must employ a free (C) function
 *  or a static class member function as the thread entry function.
 *
 *  This program must be compiled with a multi-threaded C run-time
 *  (/MT for LIBCMT.LIB in a release build or /MTd for LIBCMTD.LIB
 *  in a debug build).
 *
 *                                      John Kopplin  7/2006
 */


#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;
  }

  // In C++ you must employ a free (C) function or a static
  // class member function as the thread entry-point-function.
  // Furthermore, _beginthreadex() demands that the thread
  // entry function signature take a single (void*) and returned
  // an unsigned.
  static unsigned __stdcall ThreadStaticEntryPoint(void * pThis)
  {
      ThreadX * pthX = (ThreadX*)pThis;   // the tricky cast
      pthX->ThreadEntryPoint();           // now call the true entry-point-function

      // A thread terminates automatically if it completes execution,
      // or it can terminate itself with a call to _endthread().

      return 1;          // the thread exit code
  }

  void ThreadEntryPoint()
  {
     // This is the desired entry-point-function but to get
     // here we have to use a 2 step procedure involving
     // the ThreadStaticEntryPoint() function.

    for (int i = loopStart; i <= loopEnd; ++i)
    {
      if (i % dispFrequency == 0)
      {
          printf( "%s: i = %d\n", threadName.c_str(), i );
      }
    }
    printf( "%s thread terminating\n", threadName.c_str() );
  }
};


int main()
{
    // All processes get a primary thread automatically. This primary
    // thread can generate additional threads.  In this program the
    // primary thread creates 2 additional threads and all 3 threads
    // then run simultaneously without any synchronization.  No data
    // is shared between the threads.

    // We instantiate an object of the ThreadX class. Next we will
    // create a thread and specify that the thread is to begin executing
    // the function ThreadEntryPoint() on object o1. Once started,
    // this thread will execute until that function terminates or
    // until the overall process terminates.

    ThreadX * o1 = new ThreadX( 0, 1, 2000 );

    // When developing a multithreaded WIN32-based application with
    // Visual C++, you need to use the CRT thread functions to create
    // any threads that call CRT functions. Hence to create and terminate
    // threads, use _beginthreadex() and _endthreadex() instead of
    // the Win32 APIs CreateThread() and EndThread().

    // The multithread library LIBCMT.LIB includes the _beginthread()
    // and _endthread() functions. The _beginthread() function performs
    // initialization without which many C run-time functions will fail.
    // You must use _beginthread() instead of CreateThread() in C programs
    // built with LIBCMT.LIB if you intend to call C run-time functions.

    // Unlike the thread handle returned by _beginthread(), the thread handle
    // returned by _beginthreadex() can be used with the synchronization APIs.

    HANDLE   hth1;
    unsigned  uiThread1ID;

    hth1 = (HANDLE)_beginthreadex( NULL,         // security
                                   0,            // stack size
                                   ThreadX::ThreadStaticEntryPoint,
                                   o1,           // arg list
                                   CREATE_SUSPENDED,  // so we can later call ResumeThread()
                                   &uiThread1ID );

    if ( hth1 == 0 )
        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 );

    // The System::Threading::Thread object in C++/CLI has a "Name" property.
    // To create the equivalent functionality in C++ I added a public data member
    // named threadName.

    o1->threadName = "t1";

    ThreadX * o2 = new ThreadX( -1000000, 0, 2000 );

    HANDLE   hth2;
    unsigned  uiThread2ID;

    hth2 = (HANDLE)_beginthreadex( NULL,         // security
                                   0,            // stack size
                                   ThreadX::ThreadStaticEntryPoint,
                                   o2,           // arg list
                                   CREATE_SUSPENDED,  // so we can later call ResumeThread()
                                   &uiThread2ID );

    if ( hth2 == 0 )
        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";

    // If we hadn't specified CREATE_SUSPENDED in the call to _beginthreadex()
    // we wouldn't now need to call ResumeThread().

    ResumeThread( hth1 );   // serves the purpose of Jaeschke's t1->Start()

    ResumeThread( hth2 );//你需要恢复线程的句柄 使用该函数能够激活线程的运行

    // In C++/CLI the process continues until the last thread exits.
    // That is, the thread's have independent lifetimes. Hence
    // Jaeschke's original code was designed to show that the primary
    // thread could exit and not influence the other threads.

    // However in C++ the process terminates when the primary thread exits
    // and when the process terminates all its threads are then terminated.
    // Hence if you comment out the following waits, the non-primary
    // threads will never get a chance to run.

    WaitForSingleObject( hth1, INFINITE );
    WaitForSingleObject( hth2, INFINITE );
           //WaitForSingleObject 函数用来检测 hHandle 事件的信号状态,当函数的执行时间超过 dwMilliseconds 就返回,
     //但如果参数 dwMillisecondsINFINITE 时函数将直到相应时间事件变成有信号状态才返回,否则就一直等待下去
     //,直到 WaitForSingleObject 有返回直才执行后面的代码

    GetExitCodeThread( hth1, &dwExitCode );
    printf( "thread 1 exited with code %u\n", dwExitCode );

    GetExitCodeThread( hth2, &dwExitCode );
    printf( "thread 2 exited with code %u\n", dwExitCode );
         //

//GetExitCodeThread这个函数是获得线程的退出码,  第二个参数是一个 DWORD的指针,
//用户应该使用一个 DWORD 类型的变量去接收数据,返回的数据是线程的退出码,
//第一个参数是线程句柄,用 CreateThread 创建线程时获得到。
//通过线程退出码可以判断线程是否正在运行,还是已经退出。


    // The handle returned by _beginthreadex() has to be closed
    // by the caller of _beginthreadex().

    CloseHandle( hth1 );
    CloseHandle( hth2 );

    delete o1;
    o1 = NULL;

    delete o2;
    o2 = NULL;

    printf("Primary thread terminating.\n");
}

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

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

3) _beginthreadex函数的参数列表与CreateThread函数的参数列表是相同的,但是参数名和类型并不完全相同。这是因为Microsoft的C/C++运行期库的开发小组认为,C/C++运行期函数不应该对Windows数据类型有任何依赖。_beginthreadex函数也像CreateThread那样,返回新创建的线程的句柄。
下面是关于_beginthreadex的一些要点:
&8226;每个线程均获得由C/C++运行期库的堆栈分配的自己的tiddata内存结构。(tiddata结构位于Mtdll.h文件中的VisualC++源代码中)。

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

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

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

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

4) _endthreadex的一些要点:
&8226;C运行期库的_getptd函数内部调用操作系统的TlsGetValue函数,该函数负责检索调用线程的tiddata内存块的地址。

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

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

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

7)更多的API:

HANDLE GetCurrentProcess();

HANDLE GetCurrentThread();

DWORD GetCurrentProcessId();

DWORD GetCurrentThreadId()。

DWORD SetThreadIdealProcessor(HANDLE hThread,DWORD dwIdealProcessor);

BOOL SetThreadPriority(HANDLE hThread,int nPriority);

BOOL SetPriorityClass(GetCurrentProcess(),  IDLE_PRIORITY_CLASS);

BOOL GetThreadContext(HANDLE hThread,PCONTEXT pContext);BOOL SwitchToThread();

三注意
1)C++主线程的终止,同时也会终止所有主线程创建的子线程,不管子线程有没有执行完毕。所以上面的代码中如果不调用WaitForSingleObject,则2个子线程t1和t2可能并没有执行完毕或根本没有执行。
2)如果某线程挂起,然后有调用WaitForSingleObject等待该线程,就会导致死锁。所以上面的代码如果不调用resumethread,则会死锁。

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

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

你也许会说我一直用CreateThread来创建线程,一直都工作得好好的,为什么要用_beginthreadex来代替CreateThread,下面让我来告诉你为什么。
回答一个问题可以有两种方式,一种是简单的,一种是复杂的。
如果你不愿意看下面的长篇大论,那我可以告诉你简单的答案:_beginthreadex在内部调用了CreateThread,在调用之前_beginthreadex做了很多的工作,从而使得它比CreateThread更安全。

C++多线程实例(_beginThreadex创建多线程)相关推荐

  1. _beginThreadex创建多线程解读

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

  2. 多线程Thread类创建多线程

    package com.ajax; //多线程Thread类创建多线程 public class Example02 {public static void main(String[] args){n ...

  3. 多线程Runnable类创建多线程

    package com.ajax; //多线程Runnable类创建多线程 public class Example01 {public static void main(String [] args ...

  4. python如何开启多线程_Python如何创建多线程

    Python提供了_thread和threading两个模块来支持多线程,但_thread提供低级别的.原始的县城支持,以及一个简单的锁,通常情况下我们使用后者,来进行多线程编程 创建多线程 使用th ...

  5. Java面试之多线程:Java创建多线程为什么只有一种方式?

    关于线程的创建,其实我一直都有话说,于是今天,我来开个头.今天依旧一样,长话短说,只谈创建多线程.你好,我是fntp!今天要跟大家分享的是博主最近面试Java实习所遇到的一些问题!那就是经常性被问到的 ...

  6. _beginthreadex 创建多线程解读

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

  7. _beginthreadex创建多线程的用法

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

  8. Qt多线程学习:创建多线程

    [为什么要用多线程?] 传统的图形用户界面应用程序都只有一个执行线程,并且一次只执行一个操作.如果用户从用户界面中调用一个比较耗时的操作,当该操作正在执行时,用户界面通常会冻结而不再响应.这个问题可以 ...

  9. Objecttive-C 创建多线程

    在Objecttive-C里创建多线程一般有两种方法, 一种是initWithTarget,还有一种是detachNewThreadSelector. 下面是两个实例,创建多线程的实例,支持传递参数. ...

最新文章

  1. 后勤问题怎么办。。。(求刊登)
  2. Altium Desgner软件,PCB设计中铺铜的作用
  3. [bzoj1797][Ahoi2009]Mincut 最小割
  4. markdown pad激活
  5. php强类型作用,php类、接口、抽象、trait、强类型使用
  6. vue 刷新echarts_在vue.js中使用echarts,数据动态刷新
  7. html网页设计插入音乐,背景音乐是如何插入的?-网页设计,HTML/CSS
  8. 二维码加logo demo
  9. 使用Subversion进行版本控制
  10. 陶哲轩实分析 3.4 补充
  11. 深度、广度优先遍历算法python
  12. python写出雷霆战机_利用Python自制雷霆战机小游戏,娱乐编程,快乐学习!
  13. b站如何一次性把up主全部取消关注,让自己去学习
  14. UE4 凹多边形渲染
  15. 实际开发中,是否会在数据库表中设置外键?
  16. GPS模块的工作原理是怎样的
  17. 二分三分法--+++这一天的纪录
  18. 高德地图手动输入地址自动导航+webview加载JS
  19. 什么是fail-fast和fail-safe
  20. 华为android是什么型号,华为手机机型众多,目前这几款最值得入手

热门文章

  1. 【Hibernate】Hibernate系列6之HQL查询
  2. sql语句查询数据库返回结果转换显示自定义字段
  3. 转:Firebird 数据访问组件 (Delphi)
  4. TCP/IP(六):HTTP 与 HTTPS 简介
  5. pep 8 规范的一些记录
  6. [POI2007]POW-The Flood
  7. android 入门-android自定义控件
  8. redis windows下的环境搭建
  9. 错误 0xc0202049: 数据流任务 1: 无法在只读列“ID”中插入数据
  10. 共享两个做项目最常用功能操作的封装类