线程有两部分组成
(1) 线程内核对象
操作系统利用线程内核对象来管理线程。内核对象维护着线程的统计信息。
(2) 线程栈
线程栈维护着该线程内所有的函数参数和局部变量。
进程和线程的区别之处在于:进程从来不执行任何的东西,他只是为线程的执行提供一个容器。也就是说,所有的线程都必须在某个进程内运行,我们也称此为设备的上下文。这意味:
假如一个进程上下文中有两个或两个以上的线程在运行,这些线程将共享同一个地址空间。这些线程可以执行同样的代码,处理相同的数据。另外,这些线程共享 内核对象句柄,因为句柄表是针对每个进程的,而不是线程。
相较于线程,进程所使用的系统资源比较多,其原因在于地址空间。为每一个进程创建虚拟的地址空间需要大量的系统资源,系统会有很多的记录和活动。而线程只有一个线程内核对象和线程栈。
6.2 何时不应该创建线程
应用程序应该有一个用户界面线程,负责创建所有的窗口。用户界面线程的优先级应该高于工作线程,只有这样,才能保证用户界面迅速的响应用户的操作。
6.3 编写第一个线程函数
线程函数可以执行我们希望它执行的任何任务。最终,线程函数将终止并返回。此时,线程终止运行,用于线程栈的内核对象也会被释放。线程内核对象的使用计数也会递减,线程内核对象的寿命至少可以达到与他们相关的线程那样长,不过线程内核对象的寿命可能超过线程本身的寿命。
使用线程函数需要注意的是:线程函数应该尽量使用函数参数和局部变量。 使用全局变量或者静态变量时,多个线程可以同时访问这些变量,这样会破坏变量的安全性。由于函数的参数和局部变量是在线程栈上创建的。因此,不会被其他线程破坏。
我们画一个示意图:
6.4 CreateThread函数
HANDLE WINAPI CreateThread( __in LPSECURITY_ATTRIBUTES lpThreadAttributes, __in SIZE_T dwStackSize, __in LPTHREAD_START_ROUTINE lpStartAddress, __in LPVOID lpParameter, __in DWORD dwCreationFlags, __out LPDWORD lpThreadId );
调用该函数会创建一个内核对象,这个线程内核对象并不是线程本身。而是一个较小的数据结构,操作系统利用该结构管理线程。
系统会从进程的地址空间中分配内存给线程栈使用。新线程与负责创建的那个线程在相同的设备上下文中运行。因此,新线程可以访问进程内核对象的所有句柄、进程中的所有内存以及同一个进程中其他所有线程中的栈。这样,同一个进程中的多个线程可以很容易的相互通信。
有一点需要注意:CreateThread函数是用于创建线程的Windows函数,如果写的是C++运行库函数,就不要使用CreateThread而是使用_beginthreadex.具体的原因好象是因为CreateThread函数使用的是Windows的SDK(API)函数,使用了一些全局变量,而C++运行库函数(例如MFC)对这些全局变量进行了封装。具体的原因,请参见参考链接。
6.5 参数介绍
关于每个参数的意义,参见MSDN
Windows是抢占式多线程系统。我们举一个例子,代码描述如下:
CString str = "CYW"; void CMyMFCDlg::OnBegin() { // TODO: Add your control notification handler code here AfxBeginThread(MyFunc,this); MessageBox(str); } UINT CMyMFCDlg::MyFunc(LPVOID p ) { CMyMFCDlg* myDlg= (CMyMFCDlg*)p; Sleep(1000); str = "SHILI"; myDlg->MessageBox(str); return 0; }
观察输出的结果。另外,如果不加Sleep(1000),观察结果是怎样的!
因此,对全局变量而言,我们如果要保证变量的稳定,需要使用线程同步技术。
6.6关于几个线程函数调用的函数
创建线程的函数有很多种, 包括CreateThread、_beginthread、 _beginthreadex和AfxBeginThread. 用法在MSDN上都有详述, 这里做个笔记摘录.
1, CreateThread是不安全的,很多参考书上,都说不要用CreateThread 创建线程、并用CloseHandle来关闭这个线程,因为一些技术性的问题,会导致内存泄漏. 直接在CreateThread API创建的线程中使用sprintf,malloc,strcat等涉及CRT存储堆操作的CRT库函数是不安全的.
2, _beginthread和_beginthreadex在CreateThread上做了改进,可以安全的进行操作但是,必须在线程结束的时候相应的调用_endthread或_endthreadex.
3, beginthreadex比_beginthread更安全一些,beginthread隐式调用了CloseHandle关闭了线程句柄,而与_beginthreadex成对使用的_endthreadex则没有关闭线程的句柄,需要显示调用CloseHandle来关闭线程句柄,从逻辑上更安全.
4, 在MFC程序中,使用AfxBeginThread.
5, 在非MFC程序中,尽量使用_beginthreadex.
6.7 终止运行线程
(1) 线程函数返回;(推荐)
(2)调用ExitThread函数杀死
(3)在同一个进程或另一个进程的线程调用TerminateThread;
(4)线程所在的进程终止;
[1]http://www.cnblogs.com/lgxqf/archive/2009/02/10/1387480.html