转自:http://www.vckbase.com/index.php/wv/1660
一.前言
线程是执行路径。一个进程至少要有一个线程,也可能包含多个线程。若进程关闭了,则在进程中所有的线程也自动被销毁。当我们在一个应用程序中创建一个线程时,实际上,它是第二个线程。在C或C++中,程序的入口函数是main或wmain(Unicode版本)。在windows的运用程序中,程序的入口点是WinMain或wWinMain。当程序启动时,操作系统创建第一个线程。因此,windows是一个多任务操作系统。
二.线程函数
线程函数跟普通函数一样,它带有一个long void的指针参数。我们可以传任何类型的数据给这个void类型的指针数据。一个简单的线程函数如下:
1.
ThreadFunction(
LPVOID
param)
2.
{
3.
//do something
4.
……
5.
……
6.
//return value;
7.
}
三.线程属性
线程的优先级控制进程中线程的优先级。线程属性如下:
● 最高:THREAD_PRIORITY_HIGHEST
● 高于标准:THREAD_PRIORITY_ABOVE_NORMAL
● 标准:THREAD_PRIORITY_NORMAL
● 低于标准:THREAD_PRIORITY_BELOW_NORMAL
● 空闲:THREAD_PRIORITY_IDEL
我们可以用CreateThread函数设置线程优先级。在Win32 API函数中,我们用GetThreadPriority 和 SetThreadPriority获取和设置线程优先级;或者我们也可以用CWinThread的函数,在代码中,我们可以自由的调用它。优先级函数返回一个BOOL类型的变量。
四.各个平台下的多线程
1.C运行时库的多线程
● _beginthread
● _beginthreadex
● _endthread
● _endthreadex
以上C运行时库的函数都包含在头文件process.h中。要确保在Microsoft Visual Studio的工程设置是multithreaded DLL。在C运行时库中,通常是用_beginthread和_beginthreadex函数来创建线程。但是,这些线程有些不同。_beginthreadex有一些附加的参数,比如安全性和线程地址。我们用_beginthread来创建线程的话,要用_endthread来结束线程。_endthread将自动关闭线程的句柄。但是,若我们用_endthreadex的话,要用Win32 API的函数CloseHandle来关闭线程句柄。C运行时库包含了线程本地存储区(TLS)。我们可以用API或特定的编译代码去使用线程本地存储区。TlsAlloc,TlsFree,TlsGetValue和TlsSetValue通常用来储存指定的线程数据。Microsoft建议,假如你用了C运行时库的_beginthread函数,你就不要使用像ExitThread或者CreateThread这样的Win32 API函数。因为,假如你那样使用的话,可能会导致死锁。_beginthread在创建线程的时候使用多个参数。我们的例子是基于一个简单控制台的程序。用户键入线程数目创建线程,然后我们执行每一个线程。
01.
// Secound Thread function
02.
void
ThreadProc(
void
*param);
03.
// First thread
04.
int
main()
05.
{
06.
int
n;
07.
int
i;
08.
int
val = 0;
09.
HANDLE
handle;
10.
11.
printf
(
"\t Thread Demo\n"
);
12.
13.
printf
(
"Enter the number of threads : "
);
14.
scanf
(
"%d"
,&n);
15.
16.
for
(i=1;i<=n;i++)
17.
{
18.
val = i;
19.
handle = (
HANDLE
) _beginthread( ThreadProc,0,&val);
// create thread
20.
WaitForSingleObject(handle,INFINITE);
21.
}
22.
return
0;
23.
}
24.
25.
26.
void
ThreadProc(
void
*param)
27.
{
28.
int
h=*((
int
*)param);
29.
printf
(
"%d Thread is Running!\n"
,h);
30.
_endthread();
31.
}
主线程用Win32 API的函数WaitForSingleObject来等待另一个线程的完成。
2.MFC的多线程
CWinThread是所有线程操作的基类。MFC支持两种类型的线程:用户界面线程和工作线程。用户界面线程是基于windows消息。工作进程运行在后台进程中。CWinThread支持工作线程和用户界面线程。但是,这里只讨论工作线程。
MFC的类层次结构
CObject
CCmdTarget
CWinThread
CWinApp
在以上的类层次结构中,CWinApp应用程序类继承自CWinThread。因此假如我们创建了一个应用程序类,也同样创建了线程。假如我们创建线程的话,它是次线程。母类CObject有一些功能像是:支持系列化、运行时间类消息、支持调试。派生类CWinThread有同样的功能。经常用到的一些数据成员和成员函数如下:
数据成员:
● m_hThread – 当前线程句柄
● m_bAutoDelete – 设置线程是否自动释放
● m_nThreadID – 当前线程的ID
函数成员:
● CreateThread – 启动线程的exec执行
● SuspendThread – 挂起线程, 增加线程挂起数。
● ResumeThread – 恢复线程,减少线程堆栈数。
● SetThreadPriority – 设置线程的优先级(LOW,BELOW LOW or HIGH)。
● GetThreadPriority – 获取线程的优先级。
在MFC中,并不是所有的成员函数都是类成员。我们也可以访问一些全局函数。这些函数都以Afx开头。在MFC的线程中,AfxBeginThread和AfxEndThread是运用的最广泛的函数。我们用AfxBeginThread函数创建线程。AfxBeginThread语法如下:
1.
CWinThread* AfxBeginThread( AFX_THREADPROC ThreadProc,
LPVOID
Param,
2.
int
nPriority = THREAD_PRIORITY_NORMAL,
UINT
nStackSize = 0,
3.
DWORD
dwCreateFlags = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL );
ThreadProc是AfxBeginThread函数的第一个参数,我们在这个参数中使用线程函数的名称,在这个参数中传入void类型的参数指针,此函数的返回值类型是UINT。AfxBeginThread的其它参数是可选的。默认的优先级是THREAD_PRIORITY_NORMAL。当想要改变其优先级时,可以调用函数SetThreadPriority。我们同样也可以获得优先级。
AfxEndThread用来终止线程,AfxEndThread有一个退出代码参数列表。
01.
CwinThread *pThread = AfxBeginThread( ThreadFunction, &data);
02.
03.
UINT
ThreadFunction(
LPVOID
param)
04.
{
05.
DWORD
result =0 ;
06.
// do somthig
07.
AfxEndThread(exitCode);
08.
return
result;
09.
10.
}
3.Win32的多线程
Win32的线程使用CreateThread函数来创建,CreateThread函数的语法如下:
1.
HANDLE
CreateThread( LPSECURITY_ATTRIBUTES lpThreadAttributes,
2.
DWORD
dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress,
3.
LPVOID
lpParameter,
DWORD
dwCreationFlags,
LPDWORD
lpThreadId);
当我们想终止线程的时候有下面几种方法:
(1)使用TerminateThread函数
(2)使用ExitThread函数
(3)使用return
但是Advanced Windows建议我们使用Return方法。TerminateThread或ExitThread不能正确的清除线程堆栈。函数GetThreadTimes通常用来获取线程的运行时间。函数GetCurrentThreadID是获取当前线程的ID。Sleep指定线程休眠,单位为毫秒。比如,Sleep(1000)将使线程休眠1000毫秒。函数SwithToThread的功能是切换到其他线程。SuspendThread用来挂起一个线程。WaitForSingleObject等待一个指定的线程,直到线程完全完成它的工作。函数WaitForMultipleObject用来等待多个事件。等待的情形:更改通知、控制台输入、事件、工作、互斥、进程、信号量、线程和可等待定时器。
五.线程的优点
多线程运用程序使用100%的CPU效率。当我们创建一个进程,要需要更多的内存空间。多线程运用程序跟进程共享一个内存空间。每一个线程都包含了栈,因此,线程比进程占有的内存更少。一个进程可能或可能没包含多个线程,假如你在进程中开启了多个线程,所有的线程都共用这个进程的地址空间。