VC6.0 线程编程

线程

线程是“进程”中某个单一顺序的控制流。也称为轻量进程(lightweight process--LWP)。指运行中的程序的调度单位。一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属一个进程的其他线程共享进程所拥有的全部资源。一个线程可以创建和撤销另一个线程,同一进程中的多个线程之间可以并发执行。由于线程之间的相互制约,致使线程在运行中呈现出间断性。线程也有就绪、阻塞和运行三种基本状态。每一个程序都至少有一个线程,若程序只有一个线程,那就是程序本身。

线程是程序中一个单衣的顺序控制流程。在单个程序中同时运行多个线程完成不同的工作,成为多线程。


Windows Thread

头文件:#include<process.h>//for _beginthread

需要设置;Project-->>Setting-->>C/C++-->>Code Generation-->>User run-time library 选择Debug Multithreaded 或者Multithreaded。也可在Project Option中加入/MT 或/MTD


经典例子:

// Thread.cpp
/************************************************************************/
/* 
 主线程创建2个线程t1和t2,创建时,2个线程就被挂起,后来调用ResumeThread
 恢复2个线程,是其开始执行,调用WaitForSingleObject等待2个线程执行完,然后
 退出主线程,即结束进程。
*/
/************************************************************************/
/************************************************************************/
/* 
   One hassle  is that 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).
*/
/************************************************************************/
#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 = 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 _begintreadex() 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 number 
		named threadName.
	*/
	/************************************************************************/
    o1->threadName = "t1";

    ThreadX * o2 = new ThreadX( -100000, 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 specifed 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 就返回,但如果参数
		dwMilliseconds 为INFINITE时,函数将指导相应时间事件变成
		有信号状态才返回,否则就一直等待下去。直到,WaitForSingleObject
		有返回才执行后面代码。
	*/
	/************************************************************************/
    GetExitCodeThread( hth1, &dwExitCode );
    printf( "thread 1 exited with code %u\n", dwExitCode );

    GetExitCodeThread( hth2, &dwExitCode );
	/************************************************************************/
	/* 
		GetExitCodeThread这个函数是获得线程的退出码,第二个参数
		是一个DWORD的指针,用户应该使用一个DWORD类型的变量
		去接收数据,返回的数据是线程的退出码,第一个参数是线程句柄
		,用 CreateThread 创建线程时,获得到。
		通过线程退出码可以判断线程是否真在运行,还是已经退出。
	*/
	/************************************************************************/
    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 0;
}


NOTE:

1. 如果你在编写C/C++代码,决不应该调用CreateThread。相反,应该使用Visual C++运行时库函数_beginthreadex,退出也应该使用_endthreadex。如果不使用Microsoft的Visual C++编译器,你的编译器供应商有它自己的CreateThread替代函数。

2.因为_beginthreadex和_endthreadex是CRT(C Runtime Library)线程函数,所以必须注意编译选项runtime library的选择,使用MT或MTD 【MultiThreaded, Debug MultiThreaded】。

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


关于_beginthreadex的一些要点:

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

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

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

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

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


_endthreadex的一些要点

C运行时库的_getptd函数内部调用操作系统的TlsGetValue函数,该函数负责检索调用线程的tiddata内存块的地址。然后该数据块被释放,而操作系统的ExitThread函数被调用,以便真正撤销该线程。当然,退出代码要正确地设置和传递。


总结

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

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

更丰富的API

HANDLE GetCurrentProcess();

HANDLE GetCurrentThread();

DWORD GetCurrentProcessId();

DWORDGetCurrentThreadId();

DWORDSetThreadIdealProcessor(HANDLE hThread, DWORD dwldealProcessor);

BOOL SetPriorityClass(GetCurrentProcess(), IDLE_PRIORITY_CLASS);

BOOL SetThreadPriority(HANDLE hTread, in nPriotiry);

BOOL GetThreadContext(HANDLE hThread, PCONTEXT pContext);

BOOL SwitchToThread();


重点关注:

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

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


_beginthreadex OR CreateThread & Why

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

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

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

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

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

发布了42 篇原创文章 · 获赞 1 · 访问量 3万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章