1.CreateThread
产生一个线程(并因而成就一个多线程程序),是以CreateThread()作为一切行动的开始,次函数的原型如下:
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,
SIZE_T dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId
);
参数
lpThreadAttributes:描述施行于这一新线程的security属性。NULL表示使用缺省值。
dwStatckSize : 新线程拥有自几的堆栈,0表示使用缺省大小。
lpStartAddress: 新线程将开始的起始地址,这是一个函数指针(在C语言中函数名成即代表函数指针)。
lpParameter:此值将被传送到上述所指定的新线程函数去,作为参数(唯一的参数)。
dwCreationFlags:允许你产生一个暂时挂起的线程,默认情况是“立即开始执行”。
lpThreadId :新线程的ID会被传回到这里。
返回值:
如果CreateThread()成功,传回一个handle,代表新线程,否则传回一个FALSE。如果失败,你可以调用GetLastError()获知原因。
备注:这个函数的运行方式可能会让你在领悟过程中遇到一些棘手的地方,因为它不同于任何其他传统程序。
面对一个函数的调用操作,控制权会转移到被调用函数中,执行完毕后在返回原调用处。
而产生一个线程,情况与函数调用类似,但是有点曲折,下面以简单的程序为例:
#include
DWORD WINAPI ThreadFunc(LPVOID);
void main()
{
HANDLE hThread;
DWORD threadId;
hThread = CreateThread(NULL,
0,
ThreadFunc,
0,
0,
&threadId);
printf("Thread running");
}
DWORD WINAPI ThreadFunc(LPVOID p)
{
//...
}
我们不是直接调用线程函数ThreadFunc(),而是调用CreateThread()并交给它线程函数ThreadFunc()的地址。CreateThread()开启一个新的线程,该线程调用ThreadFunc函数,而原来的线程继续前进,换句话说,ThreadFunc()被异步地(asynchronously)执行了,意思是ThreadFunc()不需要在main()继续前进之前完成。所以,返回值也就不可能像传统方式那样。
并不是ThreadFunc()这个函数有什么魔力。你的线程起始函数可以任意命名,其间的关系完全靠你交给CreateThread()的函数地址而建立。
一旦ThreadFunc()期待能够,它就全独立于原调用端了。
备注:1.请特别注意ThreadFunc()的函数定义:
DWORD WINAPI ThreadFunc(LPVOID p)
CreateThread()的函数声明中期望第三个参数是个函数指针,指向某种特定类型的函数:返回值为DWORD,调用约定是WINAPI,有一个LPVOID参数,换句话说,如果你的线程函数不符合这些要求,编译时便会出现错误信息。一开始就精准得生命正确的函数类型,比日后才强制类型转换好,不要强制类型转换,编译器就会进行适当的类型检验工作,并让你知道你的声明是否正确。
2.除了c,pascal,stdcall之外,什么时候又来了一个WINAPI调用约定(calling convention)呢?原来,WINAPI在WINDEF.H中被定义为:
#define WINAPI __stdcall