线程和调度
这篇文章纯属搬书和翻译MSDN,之所以这样做,是因为重新输入一篇,记忆能更深,这是个非常重要的主题!
线程是Windows的基本执行单元,本文讲描述线程管理以及程序并行性,使用线程简化程序设计并且增强性能以及和C++11 thread库比较(也许会加入pthreds也说不定,但我的水平太次).线程的便利带来了确实更多的同步,性能问题,这是挑战也是增强我们编程水平的机遇,这大概是计算机编程的美妙之一,
一段写在剑桥建筑墙上的话:"时间是一种设施,发明它就是为了不让所有的事情都立即发生."
为什么不是多进程并发执行(通过诸如共享内存或者管道的机制交互),但是他们有很多缺点.
{
1.操作系统切换进程昂贵又耗时
2.进程耦合度太低,难以共享资源,比如打开的文件
3.难以管理多个并发的和交互的任务,比如等待用户输入
4.I/O绑定的程序,尽管windows允许异步重叠I/O,
5.最重要的一点,现在是多处理器时代
}
前景和问题:许多人编写多线程程序难免遇到以下问题和担忧
{
1.线程共享进程内的存储和其他资源,如果一个线程无意修改了有一个线程的数据,可能会导致竞态条件或者死锁
2.在某些情况下,并发可能极大的降低性能,而不是提高性能
3.为了利用线程而转发过去的单线程程序将是个挑战,缺乏qui程序的理解
}
进程中的线程共享代码和数据,但是除了共享的数据之外线程也拥有自己独特的存储,必须注意的是,数据并没有完全与进程中的其他线程隔离,程序员必须确保一个线程不去范围指派给其他线程的数据
{
- 每个线程都有自己的堆栈用于函数调用或者(异常处理?)
- 创建线程时可向线程传递一个参数,通常是一个指针(Windows),C++11 thread可以任意
- 每个线程可分配自己的线程本地存储(TLS)索引,并且可以设置和读取TLS值
}
Windows程序可让不同线程,甚至是同一进程的不同线程,在不同处理器上来并发执行来利用多处理器,这样带来了最头痛的性能影响问题,就是缓存同步问题.意味着如果不足够小心和良好的策略来处理,那么执行结果将会比单进程的更慢
线程管理
哦,线程也有句柄(也就是HANDLE类型),而且有一个CreateThread系统调用并在调用进程的地址空间中创建可执行的线程,我们有时候会提到"父"与"子"概念,但是操作系统并不区分.CreateThread有许多独特的需求,这是C++ thread做不到的
CreateThread函数允许我们:
{
- 指定线程在进程代码中的起始地址.
- 指定堆栈尺寸,堆栈空间是从进程的虚拟地址空间中分配的,默认的堆栈尺寸是父线程的虚拟内存堆栈尺寸(通常1MB).初始时提交给堆栈的是一页,新的堆栈页面按需调交,直到堆栈达到最大尺寸,不能增长
- 将一个指针指定给线程参数,他可以是任何东西(当然.C++ thread在这点上更为直观)
- CreateThread返回线程ID值及其句柄,null句柄表示失败
}
HANDLE WINAPI CreateThread(
_ In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
_ In_ SIZE_T dwStackSize,
_In_ LPTHREAD_START_ROUTINE lpStartAddress,
_In_opt_ LPVOID lpParameter,
_In_ DWORD dwCreationFlags,
_Out_opt_ LPDWORD lpThreadId
);
lpThreadAttributes是安全属性结构,可以为null.如果是null,那么返回的句柄是不能被子线程继承的,继承父亲的安全属性,详细见下
dwStackSize是新线程堆栈的字节(B)尺寸.用0表示使用主堆栈尺寸的默认值
lpStartAddress是指向要执行的线程函数(在调用进程中),这个函数有固定的形式,参数为一个单一的指针参数,返回值为DWORD
lpParameter是作为线程参数传递的指针(将被传递给线程函数),它有线程及父线程来解释,通常指向一个自定义的参数结构
dwCreateFlags有以下选择
值 |
意义 |
0 |
表示线程已经准备好并立即执行 |
CREATE_SUSPENDED 0x00000004 |
新线程将处于挂起状态,直到调用ResumeThread才转移到准备好状态 |
STACK_SIZE_PARAM_IS_A_RESERVATION 0x00010000 |
dwStackSize 参数指定的是栈初始化保留大小,这个参数没有指定的话 dwStackSize 指定的是提交大小. |
lpThreadId指向一个接受Id的DWORD,可以为NULL,表示无需 返回线程ID
除了这个函数,还有个有意思的函数,CreateRemoteThread函数可让线程在另外一个进程中被创建!,这个函数多出一个HANDLE参数表示进程句柄,当然函数地址必须位于目标进程的地址空间中,这是个有趣的而且有潜在危险直接影响其他进程的函数。尤其在编写调试器他会很有用
关于返回值,NULL表示失败,但是lpStartAddress就算是指向数据或者其他东西,这个函数也有可能成功
附加信息(以后详细介绍)
能创建的最大线程数被可用虚拟内存限制。默认情况下,每个线程都有一个兆字节的堆栈空间。因此,可以创建最多 2,048个 线程。如果减少默认堆栈大小,可以创建更多的线程。。
线程安全属性在xp上有特别的行为,但是xp不在讨论范围内,并且影响OpenProcess和GetCurrentThread的函数成功与否,当然自身拥有完全的访问权限
创建线程时如果不是挂起,线程能在CreateThread返回前运行,使用ExitThread退出线程,使用GetExitCodeThread获取返回值,注意,已终止的线程会继续存在,直到最后一个引用它的句柄通过CloseHandle关闭,如果线程还在运行,这个值将是STILL_ACTIVE
使用GetThreadPriority和SetThreadPriority函数来获取和设置线程的优先级值。
当一个线程终止时,线程对象获得满足任何对象等待的线程的终止的状态。
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。