_beginthreadex与createthread和AfxBeginThread的区别

1、不要在一个MFC程序中使用_beginthreadex()或CreateThread()。 
这句话的意思是由于AfxBeginThread()是MFC封装的启动线程的函数,里面包含了很多和MFC相关的启动信息,而且封装了一些常用的操作,使用起来也比较简便。而用另外两个函数就需要程序员对类型,安全性检查进行更多的思考! 
2、用_beginthreadex()函数应该是最佳选择,因为_beginthreadex()函数是C Run-time Library 中的函数,函数的参数和数据类型都是C Run-time Library中的类型,这样在启动线程时就不需要进行Windows数据类型和C Run-time Library中的数据类型之间的转化。减低了线程启动时的资源消耗和时间的消耗! 
3、在C++程序中,几乎都要用到new和delete,难道只有使用_beginthreadex()? 
不,因为MFC也是C++类库(只不过是Microsoft的C++类库,不是标准的C++类库),在MFC中也封装了new和delete两中运算符,所以用到new和delete的地方不一定非要使用_beginthreadex() 函数,用其他两个函数都可以! 
其实在程序中使用上面的哪个函数并不是绝对的,书的作者只不过是提了一个更佳的搭配方法,我在 
MFC程序中也经常使用_beginthreadex()和CreateThread()这两个函数,运行的效果也没有多大的区别,有的时候只是需要你额外的进行一些类型检查和其他的一些转化操作,其余没有其他不妥!
简言之:
 AfxBeginThread是MFC的全局函数,是对CreateThread的封装。
 CreateThread是Win32 API函数,前者最终要调到后者。



摘录1:
 CreateThread函数是windows提供的API函数,是SDK的标准形式,在使用的过程中要考虑到进程的同步与互斥的关系,进程间的同步互斥等一系列会导致操作系统死锁的因素,用起来比较繁琐一些,初学的人在用时可能会产生不可预料的错误,建议多用AfxBeginThread,是编译器对原来的CreateThread函数的封装,用于MFC编程(当然,只要修改了项目属性,console和win32项目都能调用)而_beginthread是C的运行库函数。
 使用AfxBeginThread时,线程函数的定义为:UINT _yourThreadFun(LPVOID pParam),参数必须如此。
 使用CreateThread时,线程的函数定义为: DWORD WINAPI _yourThreadFun(LPVOID pParameter)。
 两者实质是一样的,不过AfxBeginThread返回CWinThread指针,就是说它会new一个CWinThread对象,而这个对象在线程运行结束时是会自动删除的,给我们带来的不便是无法获得它的状态,因为随时都有可能这个指针指向的是一个已经无效的内存区域,所以使用时(如果需要了解它的运行状况的话)首先CREATE_SUSPENDED让他挂起,然后m_bAutoDelete=FALSE,接着才ResumeThread,最后不要了delete那个指针。
 CreatThread就方便多了,它返回的是一个句柄,如果你不使用CloseHandle的话就可以通过它安全的了解线程状态,最后不要的时候CloseHandle,Windows才会释放资源,所以我一般使用CreatThread,方便。
摘录2:
 用MFC编程,不要用CreateThread,如果只是使用Runtime Library,用_BegingThread,总之,不要轻易使用CreateThread。这是因为在MFC和RTL中的函数有可能会用到些它们所封装的公用变量,也就是说AfxBeginThread和_BeginThread都有自己的启动代码是CreateThread所没有的。在用CreateThread所创建的线程中使用MFC的类和RTL函数就有可能出现问题。如果你是用汇编编写win32程序并且在线程函数中也不调用MFC和RTL的函数,那用CreateThread就没问题,或者你虽然是用C写线程函数,但你很小心没调用RTL函数也不会有问题。
  CreateThread是由操作系统提供的接口,而AfxBeginThread和_BeginThread则是编译器对它的封装。
  在可能的情况下,不要调用_beginthread,而应该调用_beginthreadex。以及对应的 _endthreadex。这都是C++运行期函数。但是使用_beginthread,无法创建带有安全属性的新线程,无法创建暂停的线程,也无法获得线程ID,_endthread的情况类似,它不带参数,
  这意味这线程的退出代码必须硬编码为0。这两个函数在_beginthreadex和_endthreadex中进行调用。CreateThread不要进行直接调用。


参考1:http://msdn.microsoft.com/en-us/library/s3w9x78e(v=vs.80).aspx
AfxBeginThread 
Call this function to create a new thread.


CWinThread* AfxBeginThread(
   AFX_THREADPROC pfnThreadProc,
   LPVOID pParam,
   int nPriority = THREAD_PRIORITY_NORMAL,
   UINT nStackSize = 0,
   DWORD dwCreateFlags = 0,
   LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL 
);


CWinThread* AfxBeginThread(
   CRuntimeClass* pThreadClass,
   int nPriority = THREAD_PRIORITY_NORMAL,
   UINT nStackSize = 0,
   DWORD dwCreateFlags = 0,
   LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL 
);


Parameters
pfnThreadProc
Points to the controlling function for the worker thread. Cannot be NULL. This function must be declared as follows:
UINT __cdecl MyControllingFunction( LPVOID pParam );
pThreadClass
The RUNTIME_CLASS of an object derived from CWinThread.
pParam
Parameter to be passed to the controlling function as shown in the parameter to the function declaration in pfnThreadProc.
nPriority
The desired priority of the thread. If 0, the same priority as the creating thread will be used. For a full list and description of the available priorities, see SetThreadPriority in the Platform SDK.
nStackSize
Specifies the size in bytes of the stack for the new thread. If 0, the stack size defaults to the same size stack as the creating thread.
dwCreateFlags
Specifies an additional flag that controls the creation of the thread. This flag can contain one of two values:
CREATE_SUSPENDED   Start the thread with a suspend count of one. Use CREATE_SUSPENDED if you want to initialize any member data of the CWinThread object, such as m_bAutoDelete or any members of your derived class, before the thread starts running. Once your initialization is complete, use CWinThread::ResumeThread to start the thread running. The thread will not execute until CWinThread::ResumeThread is called.

如果需要初始化CWinThread的数据成员,比如m_bAutoDelete,或者你的派生类中的其它成员,要在线程开始运行之前进行。一旦你的初始化完成了,可以调用CWinThread::Resume来运行这个线程。
0   Start the thread immediately after creation.


lpSecurityAttrs
Points to a SECURITY_ATTRIBUTES structure that specifies the security attributes for the thread. If NULL, the same security attributes as the creating thread will be used. For more information on this structure, see the Platform SDK.


Return Value
Pointer to the newly created thread object, or NULL if a failure occurs.


Remarks
The first form of AfxBeginThread creates a worker thread. The second form creates a user-interface thread.
AfxBeginThread creates a new CWinThread object, calls its CreateThread function to start executing the thread, and returns a pointer to the thread. Checks are made throughout the procedure to make sure all objects are deallocated properly should any part of the creation fail. To end the thread, call AfxEndThread from within the thread, or return from the controlling function of the worker thread.
Multithreading must be enabled by the application; otherwise, this function will fail. For more information on enabling multithreading, refer to /MD, /MT, /LD (Use Run-Time Library) under Visual C++ Compiler Options.
For more information on AfxBeginThread, see the articles Multithreading: Creating Worker Threads and Multithreading: Creating User-Interface Threads.

Example
See the example for CSocket::Attach.


MFC Macros and Globals 


Safely using this function
There is a bug which prevents waiting on a thread unless a thread is initialised in the following way. 
CWinThread* thread=AfxBeginThread(DL_Enroll_Base, &ARGUMENT, THREAD_PRIORITY, STACK_SIZE, CREATE_SUSPENDED);
thread->m_bAutoDelete=FALSE;
thread->ResumeThread();
See http://members.cox.net/doug_web/threads.htm for more info


参考2:http://members.cox.net/doug_web/threads.htm
This document answers some common questions concerning multithreaded programming in Visual C++, both with MFC and at the Windows API level. It's structured mainly as a conversation, in which one question and answer leads to the next, so you'll get the most out of it if you read it as a whole. To give you a quick idea of what I'm going to talk about, the questions are:
Q1   How can I safely use MFC's CWinThread class?
Q2 Should I join with my secondary threads before exiting my program, or is it OK to let the system terminate them for me?
Q3 What are the advantages to using an event synchronization object over a simple bool to ask a thread to exit?
Q4 How can I ask a thread to exit and wait for it, anyway?
Q5 How can I make my program wait for a thread to exit without blocking its user interface or sacrificing its ability to process messages and wait on other objects?
Q6 What's the correct way to use MsgWaitForMultipleObjects?
Q7 How can I control the user's interaction with my program in my MsgWaitForMultipleObjects loop?
Q8 How can my thread notify another thread or a window that it has exited?


Q1. How can I safely use MFC's CWinThread class?
A. To safely use CWinThread, you must start the thread suspended and set the CWinThread object's m_bAutoDelete member to false or DuplicateHandle a copy of its m_hThread member. Only then should you resume the thread. This avoids the race condition resulting from the default auto-delete behavior, allowing you to wait on the thread handle, something you need to do for reasons given in Q2 below. 

要安全地使用CWinThread,你必须建立一个suspended的thread,并将它的m_bAutoDelete成员设置成false。或者DuplicateHandle一个m_hThread成员的拷贝,然后resume这个“拷贝”出来的线程。这就避免了默认的auto-delete所带来的race condition,从而允许你在这个线程句柄上等待。

Briefly, the race condition arises because by default, a CWinThread object deletes itself when its associated thread terminates, invalidating any pointer to the CWinThread object you've kept around. Because this deletion occurs in the secondary thread, it runs asynchronously to the thread that created the CWinThread object, and unless you're very, very careful, the CWinThread pointer you kept can be invalidated while you're still using it. If that wasn't bad enough, according to theWaitForSingleObject documentation, it's undefined for a handle to be closed while you're waiting on it. Thus, all code which waits on a CWinThread object's m_hThread member while the object auto-deletes itself is undefined, because auto-deletion also closes m_hThread.
So what are the consequences of starting the thread suspended and dealing with this problem? If you set m_bAutoDelete to false, you become the owner of the CWinThread object and assume the responsibility for deleting it. On the other hand, if you duplicatem_hThread, you will have to close the duplicated handle when you're done with it. I find it easier just to change m_bAutoDelete.


Q2. Should I join with my secondary threads before exiting my program, or is it OK to let the system terminate them for me?
A. In general, it's a bad idea to allow secondary threads to continue executing as the application is shutting down. By "application", I mean the primary thread, which is the thread that initializes and destroys global data and runs the mainfunction. (Although I'm talking about console program functions such as main, everything I say here applies to GUI programs, too.) After you return from main or call exit, the CRT takes over, destroys static duration objects, and performs other termination duties, until the CRT finally calls ExitProcess, which forcibly terminates all remaining secondary threads. The problem is, these secondary threads have continued to execute oblivious to the fact the environment in which they're running and the data they may be using are being destroyed all around them. Think Rome, Nero, and a fiddle, and you'll get the picture. To avoid this and achieve an orderly shutdown, you have to join with all your threads before exiting your program, and that means being able to wait on their handles. For CWinThread, this brings us back to Q1 and the necessity of disabling auto-deletion. This whole auto-deletion concept as applied to threads is pretty much a bad idea, with limited utility.


Q3. What are the advantages to using an event synchronization object over a simple bool to ask a thread to exit?
A. Events are much more flexible. They support polling with WaitForSingleObject, but you can also use them inWaitForMultipleObjects. This comes in handy when you'd like to wait forever on some other object but retain the ability to exit quickly when asked. You can't use WaitForSingleObject(hObj, INFINITE), because you'd miss thread exit requests that are made before the object you're waiting on is signaled. Some people whose use boolean quitNow variables solve this problem by waiting a second or two and polling their quitNow variable in a loop. But if you use an event and store it as the first object in theWaitForMultipleObjects handle array, you can wait forever, which means you wait more efficiently, and you normally don't need the while (!quitNow) loop. My code tends to look like this, where WaitAny is a convenient interface to WaitForMultipleObjects:
// WaitAny is overloaded on up to 5 WaitableHandles, and it waits forever.
// quitEvent is an Event object, and Event is derived from WaitableHandle.
switch (WaitAny(quitEvent, other WaitableHandles))
{
case WAIT_OBJECT_0 :
   clean up and exit;
...other cases
}
This is considerably simpler than constructing a loop to poll a bool at regular intervals, and it's more efficient to boot, especially if you can end up waiting for a long time.


Q4. How can I ask a thread to exit and wait for it, anyway?
A. Assuming you've read everything above, the following should make sense:
// The variable pThread points to a CMyThread that was started
// suspended, and whose m_bAutoDelete member was set to false,
// as described in Q1. The class CMyThread was derived from
// CWinThread and contains an event variable called quitEvent.
SetEvent(pThread->quitEvent);
WaitForSingleObject(pThread->m_hThread, INFINITE);
delete pThread;
This works great if you've designed the secondary thread to exit quickly once quitEvent has been set. However, the waiting thread cannot proceed until the other thread has terminated, so this sequence should not be used unless you can guarantee a timely exit, or it's acceptable to wait indefinitely. For an MFC program's main GUI thread, it's typically not acceptable to wait indefinitely in WaitForSingleObject, because it prevents the program from processing messages, which makes it unresponsive to user input, redraw requests, and so forth.


Q5. How can I make my program wait for a thread to exit without blocking its user interface or sacrificing its ability to process messages and wait on other objects?
A. The only way is to resume a message loop, which destroys the modality of what you're currently doing. You must account for this change and keep your program in a good state. For example, you may have to guard against creating another thread until the current one finishes. You'll have to deal with the user trying to quit your program while the thread is running. And so on. Once you've accounted for all these things, your primary thread still needs to join with the secondary thread and delete theCWinThread object when the thread has terminated. The easiest way is to do this in response to a custom message posted from the secondary thread to a window belonging to the main thread; you would typically use PostMessage for this. In some cases, you might prefer to wait on the thread handle in a MsgWaitForMultipleObjects loop, because it lets you vet the incoming messages locally, which relieves you from modifying your main message loop, which in many applications, isn't readily available to modify, because it's buried deep inside a library like MFC. If you do return to a global message loop, you'll have to maintain a flag that other parts of your program can inspect, so they can decide how to process a given message while your program is in this waiting for a thread to exit state.


Q6. What's the correct way to use MsgWaitForMultipleObjects?
A. First, you have to understand the new input issue. The documentation says:
MsgWaitForMultipleObjects does not return if there is unread input of  the specified type in the message queue after the thread has called a function to check the queue. This is because functions such as PeekMessage, GetMessage, GetQueueStatus, and WaitMessage check the queue and then change the state information for the queue so that the input is no longer considered new. A subsequent call to MsgWaitForMultipleObjects will not return until new input of the specified type arrives. The existing unread input (received prior to the last time the thread checked the queue) is ignored.
In a nutshell, unless you're certain of everything that preceded the MsgWaitForMultipleObjects call, you need to process any queued messages in a PeekMessage loop before dropping into MsgWaitForMultipleObjects. Otherwise, MsgWaitForMultipleObjectsmight not wake up until additional input becomes available, which can take an indefinite amount of time, even though there is a message waiting for your application at the time you called MsgWaitForMultipleObjects. Here's a sketch of aMsgWaitForMultipleObjects loop for MFC; it can be slightly simplified by using MsgWaitForMultipleObjectsEx, but then you sacrifice Windows 95 compatibility:
LONG idleCount = 0; 
for (;;) 

   MSG msg; 
   if (PeekMessage(&msg, 0, 0, 0, PM_NOREMOVE)) 
      theApp.PumpMessage(); 
   else if (!theApp.OnIdle(idleCount++)) 
   { 
      idleCount = 0; 
      if (!PeekMessage(&msg, 0, 0, 0, PM_NOREMOVE)) 
      { 
         HANDLE hdls[2] = { evQuit, hWhatever }; 
         DWORD res = MsgWaitForMultipleObjects(2, hdls, 
               false, INFINITE, QS_ALLINPUT); 
         if (res == WAIT_OBJECT_0) 
            break; 
         else ... 
      } 
   } 
}
Note carefully the second PeekMessage. It's necessary because an OnIdle handler could have reset the new input flag, causingMsgWaitForMultipleObjects to stall. If you were concerned about being flooded with messages or idle-time processing, such that there might be a significant delay before MsgWaitForMultipleObjects was called, you could "poll the handles" by injecting someWaitForMultipleObjects calls with timeout parameters of zero, and thus give the handles priority.


Q7. How can I control the user's interaction with my program in my MsgWaitForMultipleObjects loop?
A. You would need to replace this code from Q6 with a version that examines incoming messages:
if (PeekMessage(&msg, 0, 0, 0, PM_NOREMOVE))
   theApp.PumpMessage();
For example, you might want to discard input and show the busy cursor while waiting, which can be appropriate if you need to process messages while waiting but disallow user input. However, the obvious approach of examining the message betweenPeekMessage and PumpMessage doesn't work. To understand why, consider that WM_PAINT is a low priority message that is deferred as long as higher priority messages are available to be processed. Thus, PeekMessage could return WM_PAINT, which is definitely a message we want to process, but PumpMessage makes its own call to GetMessage, which could retrieve a higher priority message, were one to be delivered between the PeekMessage and PumpMessage calls. This could be WM_KEYDOWN, for example, one of the messages we want to discard. My function PumpMessageDiscardingInputMessages avoids this message order inversion problem by ensuring we dispatch the exact message we peeked (and removed, another difference from the PeekMessage/PumpMessage sequence).
// For WM_KICKIDLE 
#include  


// This function returns true if there was a message in the queue.

bool
PumpMessageDiscardingInputMessages(HWND hWnd)

   // It's possible for PeekMessage to return WM_PAINT but for a subsequent 
   // GetMessage to return WM_KEYDOWN. Thus, we can't call PumpMessage 
   // directly, as it does its own GetMessage, which could cause us to
   // process WM_KEYDOWN, one of the messages we want to discard.
   MSG msg; 
   if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) 
   { 
      if (msg.message != WM_KICKIDLE) 
      { 
         if (IsInputMessage(msg) 
               && (!hWnd 
                     || msg.hwnd == hWnd 
                     || (msg.hwnd && IsChild(hWnd, msg.hwnd)))) 
            return true;
         // This is one we want to handle, so emulate PumpMessage.
         if (!AfxGetApp()->PreTranslateMessage(&msg)) 
         { 
            TranslateMessage(&msg); 
            DispatchMessage(&msg); 
         } 
      } 
      return true; 
   } 
   return false; 
}
Now, we need to update our MsgWaitForMultipleObjects loop. Below, I'll use MsgWaitForMultipleObjectsEx, which as I mentioned in Q6, simplifies things somewhat, as it allows me to eliminate the new input issue:
LONG idleCount = 0;
for (;;)
{
   if (PumpMessagesDiscardingInputMessages())
      continue;
   else if (!AfxGetApp()->OnIdle(idleCount++))
   {
      idleCount = 0;
      HANDLE hdls[2] = { evQuit, hWhatever }; 
      DWORD const res = MsgWaitForMultipleObjectsEx(
            2,
            hdls,
            INFINITE,
            QS_ALLINPUT,
            MWMO_INPUTAVAILABLE);
      if (res == WAIT_OBJECT_0) 
         break; 
      else ... 
   }
}
Two things are notable:
The PeekMessage/PumpMessage sequence was replaced by the single call to PumpMessagesDiscardingInputMessages in order not to create the conditions for the message order inversion problem described earlier.
Using the MWMO_INPUTAVAILABLE flag with MsgWaitForMultipleObjectsEx allowed me to eliminate the PeekMessage call following OnIdle, whose necessity when using plain old MsgWaitForMultipleObjects I discussed in Q6.
Finally, what's this function I used called IsInputMessage? It looks like this:
namespace {


template
inline bool
FindMsg(const UINT msg, const UINT (&array)[size])
{
   return std::find(array, array+size, msg) != array+size;
}

}


bool
IsInputMessage(const UINT msg, const bool keys, const bool mice, const bool commands)
{
   static const UINT keyMsgTab[] = 
   {
      WM_KEYDOWN,
      WM_KEYUP,
      WM_SYSKEYDOWN,
      WM_SYSKEYUP,
      WM_CONTEXTMENU
   };


   static const UINT miceMsgTab[] = 
   {
      WM_LBUTTONDOWN, WM_LBUTTONUP,
      WM_LBUTTONDBLCLK,
      WM_MBUTTONDOWN, WM_MBUTTONUP,
      WM_MBUTTONDBLCLK,
      WM_RBUTTONDOWN, WM_RBUTTONUP,
      WM_RBUTTONDBLCLK,
      WM_XBUTTONDOWN, WM_XBUTTONUP,
      WM_XBUTTONDBLCLK,
      WM_NCLBUTTONDOWN, WM_NCLBUTTONUP,
      WM_NCLBUTTONDBLCLK,
      WM_NCMBUTTONDOWN, WM_NCMBUTTONUP,
      WM_NCMBUTTONDBLCLK,
      WM_NCRBUTTONDOWN, WM_NCRBUTTONUP,
      WM_NCRBUTTONDBLCLK,
      WM_NCXBUTTONDOWN, WM_NCXBUTTONUP,
      WM_NCXBUTTONDBLCLK,
   };


   static const UINT commandMsgTab[] = 
   {
      WM_COMMAND,
      WM_SYSCOMMAND,
      WM_CONTEXTMENU
   };


   return (keys && FindMsg(msg, keyMsgTab))
         || (commands && FindMsg(msg, commandMsgTab))
         || (mice && FindMsg(msg, miceMsgTab));
}
This simple function identifies keyboard, mouse, and command messages, returning true if the message is one you're looking for, and false otherwise.


Q8. How can my thread notify another thread or a window that it has exited?
A. Sometimes it is useful for a thread to notify another thread that it exited. For example, the primary thread might want to be notified when a secondary thread has finished, so that it can free resources it allocated for use by the thread, change UI state to reflect the current "job" has finished, etc. Having the secondary thread notify the primary relieves the primary from polling the secondary to determine these things, and eliminating polling is a good thing. So how do we do it? The best way is to post a user-defined message in the WM_APP range or obtained from RegisterWindowMessage. (For more on user-defined messages, see Raymond Chen's article, Which message numbers belong to whom?) If the primary thread has created no windows but runs a single message loop to handle posted thread messages, then PostThreadMessage is a good choice. (To understand these restrictions and when that function can be a bad choice, see KB article PRB: PostThreadMessage Messages Lost When Posted to UI Thread.) Most of the time, however, the primary thread is an UI thread that manages windows, and PostThreadMessage is inappropriate for reasons given in the KB article. In such cases, the best approach is to designate a target window to receive the notification, which the secondary thread posts with PostMessage.
Of course, the secondary thread doesn't exit immediately after it PostMessages the UWM_THREADEXITED message, and for the reasons given in Q2, someone, usually the recipient of the exit message, must wait on the secondary thread to really exit. That is, your message handler should look something like this:
   LRESULT ThreadOwnerWindow::OnThreadExited(WPARAM, LPARAM lp)
   {
      CWinThread* pThread = reinterpret_cast(lp);
      threads[pThread] = 0; // 1
      WaitForSingleObject(pThread->m_hThread);
      delete pThread;
      return 0;
   }
The line commented (1) exists because the ThreadOwnerWindow owns the CWinThread object as discussed in Q1; after the thread has died, it's only appropriate for ThreadOwnerWindow to zero the pointer it holds to the thread. (You can perform this mapping however you like.) But what about the WaitForSingleObject call? Since it blocks the UI, isn't it bad? Not at all! Posting this message is the last thing the secondary thread does, and you can expect it to terminate without delay. Indeed, Q4 says about usingWaitForSingleObject in a similar situation, "This works great if you've designed the secondary thread to exit quickly," and exit quickly it does if you've designed things properly.

Q9. SendMessage vs. PostMessage



续:

afxbeginthread和createthread函数


函数功能描述:创建新的线程


函数原型:
CWinThread* AfxBeginThread( AFX_THREADPROC pfnThreadProc/*线程函数地址*/,


                            LPVOID pParam/*线程参数*/,


                            int nPriority =THREAD_PRIORITY_NORMAL/*线程优先级*/,


                            UINT nStackSize = 0/*线程堆栈大小默认为1M*/,


                            DWORD dwCreateFlags = 0,


                            LPSECURITY_ATTRIBUTES
                            lpSecurityAttrs = NULL );


CWinThread* AfxBeginThread( CRuntimeClass* pThreadClass, int nPriority = THREAD_PRIORITY_NORMAL,
UINT nStackSize = 0, DWORD dwCreateFlags = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL );


返回值:
指向新创建的线程对象。


参数:
pfnThreadProc:工作线程的函数指针,不可以为空。并且工作线程的函数必须如此声明:
               UINT MyControllingFunction( LPVOID pParam );
pThreadClass: 从CWinThread类继承来的对象的RUNTIME_CLASS指针。
pParam:    传递给工作线程函数pfnThreadProc的参数。
nPriority:  线程的优先级。如果为0,则与创建它的线程优先级相同。可以通过参考Win32 Programmer’s
               Reference中的SetThreadPriority得到所有可用的优先级列表和描述。


nStackSize:  以字节为单位指定新线程的堆栈大小。如果为0,则与创建它的线程的堆栈大小相同。


dwCreateFlags:指定一个额外的标志控制线程的产生。它可以包括下面两个值中的一个:CREATE_SUSPENDED:以挂起


                                模式开始线程并且指定挂起次数.当调用ResumeThread时,这个线程才会被执行。0:创建之后,马上执


                                行线程。lpSecurityAttrs:指向SECURITY_ATTRIBUTES结构的指针,结构中指定了线程的安全属性。如果为NULL,则与  
                 创建它的线程的安全属性相同。如果希望得到更多的有关SECURITY_ATTRIBUTES结构的信息,  
                 请参考Win32 Programmer’s Reference。
注释:
调用这个函数创建一个新的线程。第一种形式的AfxBeginThread创建一个工作线程;第二种形式创建一个用户
接口线程。
AfxBeginThread创建一个新的CWinThread对象,调用它的CreateThread函数开始执行线程并且返回指向线程的指
针。Checks are made throughout the procedure to make sure all objects are deallocated properly
should any part of the creation fail. 终止线程,可以在线程函数中调用AfxEndThread, 或者从工作线程
的函数中返回。
了解更多的有关AfxBeginThread的信息,可以参考文章 Multithreading: Creating Worker Threads 和
Multithreading: Creating User-Interface Threads in Visual C++ Programmer’s Guide.
参看:AfxGetThread
示例:
  创建一个工作线程:
UINT      WorkForce(LPVOID lpParameter); //线程函数声明
CWinThread      *pMyFirstWorker,*pMySecondWorker;
LPVOID      pParam = NULL;
int       nPriority = THREAD_PRIORITY_ABOVE_NORMAL;//默认为THREAD_PRIORITY_NORMAL
UINT       nStackSize = 0; //与创建它的线程堆栈大小相同
DWORD       dwCreateFlags = 0; //表示线程创建标志,为0表示创建后立即执行,
LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL ; //与创建它的线程安全属性相同


pMyFirstWorker=AfxBeginThread( (AFX_THREADPROC)WorkForce, pParam, nPriority , nStackSize,
dwCreateFlags , lpSecurityAttrs);
pMySecondWorker=AfxBeginThread( (AFX_THREADPROC)WorkForce, pParam);//如果采用默认值


DWORD WINAPI WorkForce( LPVOID lpParameter   // 线程所需参数,可以通过它传递数据)
{
return 0;//什么不做
}


 


CWinThread* AfxBeginThread(
   CRuntimeClass* pThreadClass,
   int nPriority = THREAD_PRIORITY_NORMAL,
   UINT nStackSize = 0,
   DWORD dwCreateFlags = 0,
   LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL
);


参数说明:
pfnThreadProc:线程函数的地址,该参数不能设置为NULL,线程函数必须定义成全局函数或者类的静态成员函数
例如:
UINT myThreadFunc(LPVOID lparam)
或者
class A
{
public:
        static UINT __stdcall myThreadFunc(LPVOID lparam);
}
之所以要定义成类的静态成员函数,是因为类的静态成员函数不属于某个类对象,这样在调用函数
的时候就不用传递一个额外的this指针.


pThreadClass:指向从CWinThread派生的子类对象的RUNTIME_CLASS


pParam:要传递给线程函数的参数


nPriority:要启动的线程的优先级,默认优先级为THREAD_PRIORITY_NORMAL(普通优先级),关于线程
 优先级的详细说明请参考Platform SDK SetThreadPriority函数说明


nStackSize:新线程的堆栈大小,如果设置为0,则使用默认大小,在应用程序中一般情况下线程的默认堆栈大小
 为1M 


dwCreateFlags:线程创建标志,该参数可以指定为下列标志
 CREATE_SUSPENDED:以挂起方式启动线程,如果你在线程启动之前想初始化一些CWinThread类中的一些成员变量
 比如:m_bAutoDelete或者你的派生类中的成员变量,当初始化完成之后,你可以使用CWinThread类的ResumeThread
 成员函数来恢复线程的运行
 如果把该标志设置为0,则表示立即启动线程
lpSecurityAttrs:指向安全描述符的指针,如果使用默认的安全级别只要讲该参数设置为NULL就可以了!


上面就是AfxBeginThread函数的简单说明,我们在使用的时候一般情况下只要指定前两个参数,其他
参数使用默认值就可以.嗯,的确,使用起来是很简单,只要这个函数一被调用,就创建了一个线程.
但是大家有没有想过,AfxBeginThread函数究竟是如何启动的线程呢?它的内部是如何实现的呢?


下面我们就来看一下AfxBeginThread函数的内部实现


//启动worker线程
CWinThread* AFXAPI AfxBeginThread(AFX_THREADPROC pfnThreadProc, LPVOID pParam,
 int nPriority, UINT nStackSize, DWORD dwCreateFlags,
 LPSECURITY_ATTRIBUTES lpSecurityAttrs)
{
#ifndef _MT
         pfnThreadProc;
         pParam;
         nPriority;
         nStackSize;
         dwCreateFlags;
         lpSecurityAttrs;


         return NULL;
#else
         ASSERT(pfnThreadProc != NULL);


         CWinThread* pThread = DEBUG_NEW CWinThread(pfnThreadProc, pParam);
         ASSERT_VALID(pThread);


         if (!pThread->CreateThread(dwCreateFlags|CREATE_SUSPENDED, nStackSize,
                  lpSecurityAttrs))
         {
                  pThread->Delete();
                  return NULL;
         }
         VERIFY(pThread->SetThreadPriority(nPriority));
         if (!(dwCreateFlags & CREATE_SUSPENDED))
                  VERIFY(pThread->ResumeThread() != (DWORD)-1);


         return pThread;
#endif //!_MT)
}


//启动UI线程
CWinThread* AFXAPI AfxBeginThread(CRuntimeClass* pThreadClass,
 int nPriority, UINT nStackSize, DWORD dwCreateFlags,
 LPSECURITY_ATTRIBUTES lpSecurityAttrs)
{
#ifndef _MT
        pThreadClass;
        nPriority;
        nStackSize;
        dwCreateFlags;
        lpSecurityAttrs;


        return NULL;
#else
        ASSERT(pThreadClass != NULL);
        ASSERT(pThreadClass->IsDerivedFrom(RUNTIME_CLASS(CWinThread)));


        CWinThread* pThread = (CWinThread*)pThreadClass->CreateObject();
        if (pThread == NULL)
                AfxThrowMemoryException();
        ASSERT_VALID(pThread);


        pThread->m_pThreadParams = NULL;
        if (!pThread->CreateThread(dwCreateFlags|CREATE_SUSPENDED, nStackSize,
                lpSecurityAttrs))
        {
                pThread->Delete();
                return NULL;
        }
        VERIFY(pThread->SetThreadPriority(nPriority));
        if (!(dwCreateFlags & CREATE_SUSPENDED))
                VERIFY(pThread->ResumeThread() != (DWORD)-1);


        return pThread;
#endif //!_MT
}


从上面的代码中可以看出AfxBeginThread所做的事情主要有以下几点:


1.在heap中配置一个新的CWinThread对象(worker线程)
代码如:CWinThread* pThread = DEBUG_NEW CWinThread(pfnThreadProc, pParam);
调用CRuntimeClass结构中的CreateObject函数创建CWinThread对象
CWinThread* pThread = (CWinThread*)pThreadClass->CreateObject();
CRuntimeClass以及MFC相关类的内部实现,详情请参考
《深入浅出MFC》侯捷著


2.调用CWinThread::CreateThread()并设定属性,使线程以挂起状态产生
pThread->CreateThread(dwCreateFlags|CREATE_SUSPENDED, nStackSize,lpSecurityAttrs);


3.设定线程的优先权
pThread->SetThreadPriority(nPriority);


4.调用CWinThread::ResumeThread
pThread->ResumeThread();


 


 AfxBeginThread和CreateThread具体区别


        具体说来,CreateThread这个 函数是windows提供给用户的 API函数,是SDK的标准形式,在使用的过程中要考虑到进程的同步与互斥的关系,进程间的同步互斥等一系列会导致操作系统死锁的因素,用起来比较繁琐一些,初学的人在用到的时候可能会产生不可预料的错误,建议多使用AfxBeginThread,是编译器对原来的CreateThread函数的封装,用与MFC编程(当然,只要修改了项目属性,console和win32项目都能调用)而_beginthread是C的运行库函数。


在使用AfxBeginThread时,线程函数的定义为:


UINT   _yourThreadFun(LPVOID   pParam);参数必须如此


在使用CreateThread时,线程的函数定义为: 


DWORD  WINAPI  _yourThreadFun(LPVOID pParameter);


        两个的实质都是一样的,不过AfxBeginThread返回一个CWinThread的指针,就是说他会new一个CWinThread对象,而且这个对象是自动删除的(在线程运行结束时),给我们带来的不便就是无法获得它的状态,因为随时都有可能这个指针指向的是一个已经无效的内存区域,所以使用时(如果需要了解它的运行状况的话)首先CREATE_SUSPENDED让他挂起,然后m_bAutoDelete=FALSE,接着才ResumeThread,最后不要了delete那个指针。     CreatThread就方便多了,它返回的是一个句柄,如果你不使用CloseHandle的话就可以通过他安全的了解线程状态,最后不要的时候CloseHandle,Windows才会释放资源,所以我一般使用CreatThread,方便。


如果用MFC编程,不要用CreateThread,如果只是使用Runtime Library,用_BegingThread,总之,不要轻易使用CreateThread。这是因为在MFC和RTL中的函数有可能会用到些它们所封装的公用变量,也就是说AfxBeginThread和_BeginThread都有自己的启动代码是CreateThread所没有的。在用CreateThread所创建的线程中使用MFC的类和RTL函数就有可能出现问题。如果你是用汇编编写win32程序并且在线程函数中也不调用MFC和RTL的函数,那用CreateThread就没问题,或者你虽然是用C写线程函数,但你很小心没调用RTL函数也不会有问题。


  CreateThread是由操作系统提供的接口,而AfxBeginThread和_BeginThread则是编译器对它的封装。


  在可能的情况下,不要调用_beginthread,而应该调用_beginthreadex。以及对应的_endthreadex。这都是C++运行期函数。但是使用_beginthread,无法创建带有安全属性的新线程,无法创建暂停的线程,也无法获得线程ID,_endthread的情况类似,它不带参数,




  这意味这线程的退出代码必须硬编码为0。这两个函数在_beginthreadex和_endthreadex中进行调用。CreateThread不要进行直接调用。 


简言之:


  AfxBeginThread是MFC的全局函数,是对CreateThread的封装。


    CreateThread是Win32 API函数,前者最终要调到后者。




1>.
具体说来,CreateThread这个 函数是windows提供给用户的 API函数,是SDK的标准形式,在使用的过


程中要考虑到进程的同步与互斥的关系,进程间的同步互斥等一系列会导致操作系统死锁的因素,用起来


比较繁琐一些,初学的人在用到的时候可能会产生不可预料的错误,建议多使用AfxBeginThread,是编译


器对原来的CreateThread函数的封装,用与MFC编程(当然,只要修改了项目属性,console和win32项目


都能调用)而_beginthread是C的运行库函数。


2>
在使用AfxBeginThread时,
线程函数的定义为:UINT   _yourThreadFun(LPVOID   pParam)参数必须如此


在使用CreateThread时,
线程的函数定义为: DWORD WINAPI _yourThreadFun(LPVOID pParameter)


两者实质是一样的,
不过AfxBeginThread返回CWinThread指针,就是说它会new一个CWinThread对象,而这个对象在线程运行结束时是会自动删除的,


CreatThread,它返回的是一个句柄,如果你不使用CloseHandle的话就可以通过它安全的了解线程状态,


最后不要的时候CloseHandle,Windows才会释放资源


你可能感兴趣的:(_beginthreadex与createthread和AfxBeginThread的区别)