线程是进程中的一个实体,是被系统独立调度和分派的基本单位。一个进程可以拥有多个线程,但是一个线程必须有一个进程。
线程自己不拥有系统资源,只有运行所必须的一些数据结构,但它可以与同属于一个进程的其它线程共享进程所拥有的全部资源,同一个进程中的多个线程可以并发执行。
(一)CreateThread
1.说明
使用CreateThread函数创建线程
2.函数原型
WINBASEAPI HANDLE WINAPI CreateThread ( LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId);
参数说明:
第一个参数 lpThreadAttributes
表示线程内核对象的安全属性,一般传入NULL表示使用默认设置。
第二个参数 dwStackSize
表示线程栈空间大小。传入0表示使用默认大小(1MB)。
栈是私有的但堆是公用的
什么是堆栈? 其实堆是堆、栈是栈, 有时 "栈" 也被叫做 "堆栈".
"栈"(或叫堆栈)适合存取临时而轻便的变量, 主要用来储存局部变量
现在我们知道了线程有自己的 "栈", 并且在建立线程时可以分配栈的大小.
第三个参数 lpStartAddress
表示新线程所执行的线程函数地址,多个线程可以使用同一个函数地址。
等线程退出后, 我们用 GetExitCodeThread 函数获取的退出码就是这个返回值!
如果线程没有退出, GetExitCodeThread 获取的退出码将是一个常量 STILL_ACTIVE (259); 这样我们就可以通过退出码来判断线程是否已退出.
第四个参数 lpParameter
是传给线程函数的参数。
线程入口函数的参数是个无类型指针(Pointer), 用它可以指定任何数据;
第五个参数 dwCreationFlags
指定额外的标志来控制线程的创建,为0表示线程创建之后立即就可以进行调度,如果为CREATE_SUSPENDED则表示线程创建后暂停运行,这样它就无法调度,直到调用ResumeThread()。
ResumeThread 恢复线程的运行; SuspendThread 挂起线程.
这两个函数的参数都是线程句柄, 返回值是执行前的挂起计数.
第六个参数 lpThreadId
将返回线程的ID号,传入NULL表示不需要返回该线程ID号。
1、线程的 ID 是唯一的; 而句柄可能不只一个, 譬如可以用 GetCurrentThread 获取一个伪句柄、可以用 DuplicateHandle 复制一个句柄等等.
2、ID 比句柄更轻便.
返回值:
CreateThread的返回值是线程的句柄,失败的话就返回NULL
3.返回线程句柄
"句柄" 类似指针, 但通过指针可读写对象, 通过句柄只是使用对象;
有句柄的对象一般都是系统级别的对象(或叫内核对象); 之所以给我们的是句柄而不是指针, 目的只有一个: "安全";
貌似通过句柄能做很多事情, 但一般把句柄提交到某个函数(一般是系统函数)后, 我们也就到此为止很难了解更多了; 事实上是系统并不相信我们.
不管是指针还是句柄, 都不过是内存中的一小块数据(一般用结构描述), 微软并没有公开句柄的结构细节, 猜一下它应该包括: 真实的指针地址、访问权限设置、引用计数等等.
既然 CreateThread 可以返回一个句柄, 说明线程属于 "内核对象".
实际上不管线程属于哪个进程, 它们在系统的怀抱中是平等的; 在优先级(后面详谈)相同的情况下, 系统会在相同的时间间隔内来运行一下每个线程, 不过这个间隔很小很小, 以至于让我们误以为程序是在不间断地运行.
这时你应该有一个疑问: 系统在去执行其他线程的时候, 是怎么记住前一个线程的数据状态的?
有这样一个结构 TContext, 它基本上是一个 CPU 寄存器的集合, 线程是数据就是通过这个结构切换的, 我们也可以通过 GetThreadContext 函数读取寄存器看看.
4.代码示例
// 创建对应的线程 for (int i = 0; i < SysInfo.dwNumberOfProcessors; i++) { HANDLE hThread = CreateThread(NULL, 0, WorkThread, this, 0, NULL); CloseHandle(hThread); }
标签:函数,Thread,CreateThread,句柄,参数,线程,ID From: https://www.cnblogs.com/imreW/p/17156552.html