《Win32 多线程程序设计》读书笔记

来源:互联网 发布:suse linux配置ip 编辑:程序博客网 时间:2024/04/28 18:49
一、多线程的诞生

人们吃饭的时候总是这样的:一边吃一边喝一边呼吸。如果我是创建人类的大自然,我肯定会选择抢先式多任务。

    如果盖房,这需要打地基、和水泥、砌墙,那就得找你个帮手了。作为主线程的我就叫了几个帮手做我的子线程直到把房子盖好。

二、线程和进程有什么不同?

        Windows中,进程是对象和句柄的巨大熔池,饱含所有资源却什么都不会做。而在UNIX中,内核黑客们为进程绑了一个主线程,即进程除了是资源集合外,天生是可以运行的。

        线程则是这巨大池子中的鱼,往返穿梭、自由且忙碌,呼吸着、左右着这些资源。

三、Contextswitch(上下文转换)的发生。

对同一对象操作是发生context switch是有风险的,    我们称之为race condition 竞争条件。

四、AtomicOperations 原子操作应运而生。

用标志位来隔离多线程也是不科学的,会生产N多行代码,发生contextswitch机会很大。原子性应发生在机器码汇编的级别上。所以我们需要一个不被操作系统中断的标志位操作。

五、线程间如何通信?

让很多人同时在厨房帮你炒几道菜,通常不是很顺利。让多线程同时处理同一件事情值得我们小心谨慎。

六、HANDLE内核对象需要释放。

七、线程内核对象与线程的不同?

八、多线程的成功:

保证与主线程有最小的接触面积。

各线程间的数据要相互分离,避免使用全局变量,避免共同使用GDI对象。

让主线程处理UI界面。

九、线程的等待:不要在Win32中使用GetExitThread() ,会对系统资源造成严重的冲击。

我们可以用waitSingleObject替代。这两个函数都在线程核心对象被激发时返回。阻塞等待比每秒访问几百万次某个函数要好得多。

十、什么是一个被激发的对象?当线程运行时,线程核心对象未激发;当线程结束时,线程核心对象被激发。

十一、      GetExitCodeThread()获得一个也终止线程的ID

十二、      waitForMultipleObject();同时等待多个监控多个内核对象的激发。

十三、      关于内核对象:

进程在被初始化时,系统为其分配一个句柄表,此句柄表只用于内核对象。句柄表的每一项记录了每个内核对象的信息,所谓信息主要包括:(句柄在句柄表表中的)索引、指向内核对象内存的地址、访问屏蔽标志、内核对象句柄继承标志

进程在初始化时句柄表是空的,每当进程创建一个内核对象时,系统就在该进程的句柄

表中找出一个空项,设置新创建的内核对象的信息。 内核对象的创建函数几乎都是形如Cerate*的形式,这些函数返回新创建的内核对象的句柄。这里返回的所谓句柄,其值实际上是该内核对象在进程句柄表中的索引,用于标识该对象在进程句柄表中的位置

Cerate*函数返回失败的话,返回值通常为NULL0),但也有一些是INVALID_HANDLE_VALUE-1)。关闭内核对象通过调用CloseHandle完成,每调用一次,内核对象使用计数递减1

十四、      msgWaitForMultipleObjects();等待内核对象或消息的激发。

十五、      不要长期锁定一个资源

十六、      critical section(临界区) 中如果线程down掉,我们无法取消这个section,如果需要这种机制可使用mutex,我们在监视线程退出时可以进行取消。

十七、      死锁。哲学家问题,应用Mutex可以解决。

十八、      Mutex(互斥器)上锁是对内核对象进行操作,花费时间要比criticalsection长,

       可以跨进程使用。

       可指定等待时间。

十九、      哲学家问题,用WaitForMultipleObjects()可同时等待多只筷子的到来。

二十、      Semaphore(信号量)可以针对一组相同个体资源,并设立一个最大计数。当计数用完之后,之后的线程就必须等待。可以具名,可被多进程访问。

二十一、             信号量(semaphore)和互斥器(Mutex)在刚建立时都要将设法不要让别的线程立即使用。如:信号量里的create时设数量为0Mutex中设当前线程占有Mutex

二十二、             注意EVENT的遗失现象。Event主要用于同步,是核心对象,他的激活它是可控制的,比较方便,多用于IOCP。可具名,被其他进程访问

第二节 新的线程控制

一、在一个线程中结束另一个线程。

TerminateThread(),会引起内存泄漏。比较危险。

二、Signal(信号)

三、优先级:GetPriorityClass() / SetPriorityClass(); 微调:GetThreadPriority()、SetThreadPriority()

第三节 overlap IO

一、可以同时读写文件的许多部分。

二、OVERLAPPED结构体保存了信息。

三、以文件HANDLE作为激发机制,可用waitForSingleObject()/ GetoverlappedResult()来等待未完的异步事件完成。别切在OVERLAPPED中保存了所需要的信息。

四、OVERLAPPED的末尾设置EVENT结构体,当异步事件完成时,操作系统可以激发次EVENT核心对象。并且采用手动,不让操作系统自动激发它,导致竞争条件。

从而可实现在等待同一个文件的多个事件好像文件被切割后用事件通知一样。

五、重叠IO的限制如下:最多同时等待64个事件。在传输小于32k的数据是平均要比普通方法多花费15%的时间。

六、IOCP:极大的发掘CPU的速率,对多CPU的服务器很适合。

七、IOCP对线程数的控制。有时可能因为客观需要实际线程数大于CPU上最大线程数。

八、IOCP中如果执行线程因为某事(如文件io)而挂起,则CPu将闲置,故应创建比CPU数多的线程数。

        创建线程数 = 执行中线程数 + 被阻塞线程数 + CP上等待着的后备线程数

九、避免返回Completion Packets。不需要IOCP返回一个包。创建OVERLAPPED结构体,初始化一个manul-reset 手工重置的EVENT结构体,放在OVERLAPPED中,并将最后一位置1.------WIN32多线程程序设计》P182

十、因为c/c++运行标准库比多线程出现的早,所以直接用CreateThread()会有同步方面的错误。因此,高手们给我们提供了beginThreadEx()来解决这个问题。

十一、       排它锁:在一个就结构体内部设立一个锁变量,任何线程访问时都要开锁,迫使多线程的同步访问变成顺序访问,带来了安全,却失去了多线程的优势。

十二、       C++_beginThreadEx()中的参数中要使用unsigned类型,可定义一个typeDefine来转换。

十三、       C++的成员函数实现线程时,相应函数因为默认需要一个this指针,但操作系统不知道,扔了一个LRARAM进去。

        解决的办法,将相应成员函数设为static。并将this作为参数。

十四、       C++类中添加CriticalSection。可用C++CriticalSection对象进行封装。

十五、       C++错误处理,

十六、       MFC中以挂起状态启动线程。CWindThread(),AfxBeginThread()

十七、       过程:

a)       创建线程,设参数为CREATE_SUSPENDED.

b)      设置CWinThread()中的m_bAutoDelet.防止创建失败后自动DELETE线程句柄。可能会产生race condition错误。

c)      调用ResumeThread()激活线程。

十八、       不要再UI线程间共享UI对象。会很卡。

十九、       MFC中的每一步都要做到错误检测

 

 

 

 

 

 

 

原创粉丝点击