写于2016年6月24日

" vim:fen:fdm=marker:fmr={{{,}}}:fdl=0:fdc=1:ts=2:sw=2:sts=2

"第一章 为什么千头万绪 {{{

线程价廉,启动快,退出快,线程间分享了大部分核心对像拥有权
如果使用多进程,最大题是如何把窗口handle交给另一进程
1.同一进程中窗口handle,所有线程都可以使用
2.不同进程,必须产生handle的副本,并且属性可以被另它进程使用

context switch
要切换不同的线程,应先切换所隶进程的内存,
然后恢复线程在CONTEXT结构中寄存器的值

也就是换一套memory context--page directory 和 page tables

race conditions
以个以上的线程同时对一个内存单位读写,因为
抢先式多任务系统中,控制权被强制移转,线程间执行次序不可预期

Atomic Operations (原子操作)    不受中断影响而完成的操作
检查标记,设立标记的CPU指令:Test和Set,只有操作系统使用

"第一章 为什么千头万绪 }}}
"第二章 线程第一次接触 {{{
"Createthread {{{

HANLE CreateThread(
        LPSECURITY_ATTRIBUTES lpThreadAttributes,
        DWORD dwStackSize,
        LPTHREAD_START_ROUTINE lpStartAddress,
        LPVOID lpParameter,
        DWORD dwCreationFlags,
        LPDWORD lpThread);

LPSECURITY_ATTRIBUTES 线程安全属性,NULL使用缺省值
dwStackSize 线程堆栈大小,0使用缺省大小:1MB
lpStartAddress 线程函数指针
lpParameter 线程函数参数
dwCreationFlags 线程启动方式
lpThreadId 返回新线程ID
返回值: 成功返回新线程handle,否则返回FALSE

"Createthread }}}
"CloseHandle {{{

BOOL CloseHandle(HANDLE hObject)
    1.此函数唯一做的就是将引用计数减一
    2.进程总是应该在适当时候关闭打开的核心对象,不要等进程结束自动清理
    3.Createthread传回的handle属于进程所有,非线程所有,有可能有一个新
        线程产生调用CloseHandle,取代原来的线程,_beginthread

"CloseHandle }}}
"GetExitCodeThread {{{

BOOL GetExitCodeThread(
        HANDLE hThread,
        LPDWORD lpExitCode
        )
    hThread     CreateThread传回的handle
    lpexitcode  接收结束代码的DWORD
返回值:
    成功TRUE,否则FALSE,线程结束lpExitCode传回结束代码,未结束值
    STILL_ACTIVE
    无法从GetExitCodeThread的返回值判断线程是否结束
    
"GetExitCodeThread }}}
"ExitThread {{{

VOID ExitThread(
        DWORD dwExitCode
        )
此函数没返回值,被调用后的线程代码再不会被执行

"ExitThread }}}
"调用约定 {{{

calling convention 调用约定
WINAPI 线程函数调用约定
#define WINAPI __stdcall

"调用约定 }}}
"多线程程序问题 {{{

1.多线程程序无法预期
2.执行次序随机      Task Switches任何时刻任何时间发生
    编译时使用/MT或/MD,使用多线程版C runtime library
    timeslice 时间片
    抢先式多任务调度中,系统给每个线程一个定量时间片,然后控制权被转移
3.线程并不总是立刻启动

"多线程程序问题 }}}
"核心对象 {{{

Kernel Objects 核心对象

线程HANDLE,与线程有关API使用
线程ID系统范围内唯一标识线程,
AttachThreadInput和PostThreadMessage需要使用
调试器,进程观察也需要

---------------------------------------------------------------------------
handle          核心对象    KERNEL32.DLL管理
handle  是指向系统内存空间的核心对象指针,完整性与安全性故不能直接使用
        核心对象可以有一个以上拥有者,可跨进程,维护引用计数,多少个和哪一
        个进程或线程是拥有者,
        创建加1,关闭减1,至0摧毁

---------------------------------------------------------------------------
画笔,画刷,DC    GDI对象     GDI32.DLL管理

---------------------------------------------------------------------------

"核心对象 }}}
"线束主线程 {{{

总是等待所有线程结束,在结束主线程

"线束主线程 }}}
"GUI线程和worker线程 {{{
    
GUI线程:
    拥有消息队列的线程.
    1.窗口消息总是被产生它的线程捕获并处理,所有对线程的所变都应由该线程完成
    2.GUI线程,不应该用于不能马上完成的工作
worker线程纯粹运算
    2.worker线程产生输入或输出信息,应授权给GUI线程,GUI线程将结果通知worker
    
"GUI线程和worker线程 }}}
"多线程程序设计关键 {{{

1.线程间数据分开,避免使用全局变量
2.不在线程间共享GDI对象
3.尽量不在线程自己结束前,强制结束它
4.让主线处理用户界面

"多线程程序设计关键 }}}
"第二章 线程的第一次接触 }}}
"第三章 快跑与等待 {{{
"Busy loop {{{
Busy loop   用for或while循环等待线程退出
对于需要面对数十个甚至数百个客户的服务器而言,就是灾难
"Busy loop }}}
"WaitForSingleObject {{{

DWORD WINAPI WaitForSingleObject(
  __in          HANDLE hHandle,
  __in          DWORD dwMilliseconds
);
hhandle 等待的handle
dwmilliseconds  最长等待时间,时间到handle未激发也返回
                INFINITE一直等到handle激发状态才返回                
                若为0,立刻返回,并检测handle状态,
                如果handle为激发状态返回WAIT_OBJECT_0,否则WAIT_TIMEOUT
返回值:
    失败:   WAIT_FAILED
    成功:   WAIT_OBJECT_0,handle变成激发状态
            WAIT_TIMEOUT,等待时间结束,handle未激发
        WAIT_ABANDONED,拥有互斥体mutex的线程结束前没释放mutex
即使等待的线程失事或强迫结束,函数还可正常运行
        
"WaitForSingleObject  }}}
"WaitForMultipleObject {{{
DWORD Waitformultipleobjects(
    DWORD nCount,
    CONST HANDLE *lpHandles,
    BOOL bWaitAll,
    DWORD dwMilliseconds
    );
nCount      handles数组元素数量,最大量是MAXIMUM_WAIT_OBJECTS
lpHandles   handles数组
bWaitAll    true,所有handles激发返回,false,任何一个激发返回
dwmMilliseconds   
            0,立刻返回,测试handles
            INFINITE,无穷等待
            最长时间,时间到没handles激发也返回
返回值:
    时间终了返回,返回值WAIT_TIMEOUT
    bWaitAll是TRUE,返回值WAIT_OBJECT_0
    bWaitAll是FALSE,返回值 - WAIT_OBJECT_0,就是被激发handle在数组下标
    如果返回值中有任何mutexes,返回值可能WAIT_ABANDONED_0到WAIT_ABANDONED_0+nCount-1
    函数失败返回WAIT_FAILED

"WaitForMultipleObject }}}
"Getmessage {{{

GetMessage  是消息的等待函数,有消息进入消息队列才返回.

"Getmessage }}}
"可以被WaitForSingleObject等待的核心对象 {{{

Signaied Objects 被激发的对象
Thread      结束时激发,运行时不激发,CreateTheaad和CreateRemoteThread产生
Process     结束时激发,运行时不激发,CreateProcess和OpenProcess返回进程Handle 
Change Notification    由FindFirstChangeNotification产生
            特定的磁盘子目录发生一个特别变化激发,
            FILE_NOTIFY_CHANGE_FILE_NAME    创建,删除,重新命名一个文件
            FILE_NOTIFY_CHANGE_DIR_NAME     创建或删除一个目录
            FILE_NOTIFY_CHANGE_ATTRIBUTES   目录及子目录的任何属性改变
            FILE_NOTIFY_CHANGE_SIZE         目录及子目录中的任何文件大小的改变
            FILE_NOTIFY_CHANGE_LAST_WRITE   目录及子目录中任何文件最后写入时间改变
            FILE_NOTIFY_CHANGE_SECURITY     目录及子录中的任何安全属性改变

Console Input   当console窗口输入缓冲区有数据可用时激发,CreateFile和GetStdFile
            获得console的handle
Event       Event对象的状态受控于SetEvent,PulseEvent,ResetEvent函数,
            Createevent和OpenEvent可以传回Event handle,
            Event对象的状态也可以被操作系统设定, 如overlapped操作
Mutex       没任何进程拥有处于激发状态,一个等待Mutex函数返回自动重置未激发,
            CreateMutex和OpenMutex获得mutex handle
Semaphore   有计数器的互斥体,可约束拥有者线程的个数,计数大于0激发,等于0不激发
            CreateSemaphore和OpenSemaphore传回semaphore handle

"可以 WaitForSingleObject等待的核心对象 }}}
"MsgWaitForMultipleObjects 同时等待消息和核心事件 {{{
  
DWORD MsgWaitForMultipleObjects(
    DWORD nCount,
    LPHANDLE pHandles,
    BOOL fWaitAll,
    DWORD dwMillseconds,
    DWORD dwWakeMask
    )
dwWakeMask 可以是:
    QS_ALLINPUT
    QS_HOTKEY
    QS_INPUT
    QS_KEY
    QS_MOUSE
    QS_MOUSEBUTTON
    QS_MOUSEMOVE
    QS_PAINT
    QS_POSTMESSAGE
    QS_SENDMESSAGE
    QS_TIMER
1.收到WM_QUIT之后,仍然会收回WM_QUIT,如果在WM_QUIT之后等待所有线程结束,
  必须处理此消息,否则窗口变迟钝
2.handles数组必须全部有效,不能有值为NULL的元素
3.如果handles被其它线程改变,应重新修改数组,重新调用MsgWaitForMultipleObjexts
4.如果想改变数组,必须使函数返回,在重新调用

"MsgWaitForMultipleObjects }}}
"第三章 快跑与等待 }}}
"第四章 线程同步 {{{
"4.1 同步和异步 {{{

Win32中进程和线程协调工作由synchronous和asynchronous组合完成

"同步和异步 }}}
"4.2 critical sections {{{
"4.2.1 Critical Sections 概念 {{{
同一时间内最多只能有一个线程可以进入 critical section,访问其中资源
Critical Section不是核心对象,没handle,使用只需要将CRITICAL_SECTION
的局部变量用InitializeCriticalSection初始化
"4.2.1 Critical Sections }}}
"4.2.2 InitializeCriticalSection {{{

VOID InitializeCriticalSection(
    LPCRITICAL_SECTION lpCriticalSection );

lpCriticalSection CRITICAL_变量指针

"4.2.2 InitializeCriticalSection }}}
"4.2.3 DeleteCriticalSection {{{
VOID DeleteCriticalSection(
    LPCRITCAL_SECTION lpCriticalSection );

lpCriticalSection 指向不再需要的CRITICAL_SECTION对象

"4.2.3 DeleteCriticalSection }}}
"4.2.4 EnterCriticalSection {{{
VOID EnterCriticalSection(
    LPCRITICALSECTION lpCriticalSection 
    );
lpCriticalSection

指向一个即将锁定的CRITICAL_SECTION变量
 
"4.2.4 EnterCriticalSection }}}
"4.2.5 LeaveCriticalSection {{{

VOID LeaveCriticalSection(
    LPCRITICALSECTION lpCriticalSection
    );
lpCriticalSection   即将解除锁CRITICAL_SECTION变量指针

"4.2.5 LeaveCriticalSection }}}
"4.2.6 优点和缺点 {{{
1.不要长时间锁住一份资源
2.不要在Critical Section中调用Sleep和WiatFor...系列函数
3.如果程序需要访问被长期锁住的资源,程序就挂住
4.Critical Section一个缺点是,没办法获知进入Critical Section中的线程是生是死,
  如果线程结束或当掉,但没调用LeaveCritcalSection,系统没有办法将该Critical Section清除
"4.2.6 优点和缺点 }}}
"Deadlock 死锁 {{{
当一段代码需要求两个以上资源时,都有潜在死锁阴影,其原因可以非常复杂,
强迫将资源锁定,all-or-nothing(要不统统获得,要求不统统没有),可以阻止死锁的发生
"Deadlock 死锁 }}}
"4.2 critical sections }}}
"4.3 Mutex {{{
"4.3.1概念 {{{
mutex MUTual EXclusion的缩写
1.锁住一个未被拥有的mutex,比同样情况的Critical Section需要求花费几乎100倍的时间
2.Mutexs可跨进程,Critical Section只能用于同一进程
3.等待mutex时,可以指定等待时间长度,Critical Section则不行
4.用独一无二的名称命mutex,因为它是系统内全局的,系统内线程,进程都可以使用
"4.3.1概念 }}}
"4.3.2 CreateMutex {{{
HANDLE CreateMutex(
    LPSECURITY_ATTRIBUTES lpMutexAttributes,
    BOOL binitialOwner,
    LPCTSTR lpName);
lpMutexAttributes 安全属性,NULL默认
bInitialOwner   TRUE调用此函数线拥有产生的mutex,可以防止race condition的发生,
                因为即使在此函数立即调用WaitForSingleObject,也有发生race condition的风险
lpName          mutex名称,但不能有(backslash,\)

成功返回handle,否则传回NULL

"4.3.2 CreateMutex }}}
"4.3.3 mutex打开,获得,等待 {{{
调用CreateMutex指定一个早已存在的mutex
调用OpenMutex可以打开一个已存在mutex名称,传回而不产生一个新mutex,
  GetLastError传回ERROR_ALREADY_EXISTS
要获得一个线程的拥有权,保用Wiat...系列函数
没有任何线程拥有mutex,这个mutex处于激发状态
当线程拥有mutex,mutex处于锁定状态,等待它的线程将处于阻塞状态
所谓mutex激发:当没有任何线程等待该mutex,而且一个线程以Wait...等待该mutex,
  该线程会短暂出现激发状态,使Wait...函数返回
"4.3.3 mutex打开,获得,等待 }}}
"4.3.4 ReleaseMutex {{{

BOOL ReleaseMutex(
  HANDLE hMutex )
hMutex 欲释放的mutex的handle

"4.3.4 ReleaseMutex }}}
"4.3.5 Mutex的拥有权和摧毁 {{{

Mutex的拥有权:
1.属于最后对此mutex进行Wait...()操作并且尚未ReleaseMutex操作的线程
2.一次只能有一个线程拥有该mutex

Mutex的摧毁:
1.mutex在其引用计数为0时被操作系统摧毁
2.每当对此mutex调用CloseHandle,或拥有它的线程结束时,mutex的计数降1.

"4.3.5 Mutex的拥有权和摧毁 }}}
"4.3.6 处理被舍弃的互斥器 {{{

Mutex的舍弃:
  1.拥有mutex的线程结束前,没有调用ReleaseMuex,该线程被认为是被舍弃的,
  2.无论是调用ExitThread结束,或是当年而结束
  3.WaitForSingleObject等待此mutex的线程会得到WAIT_ABANDONED_0通知
  4.WaitForMultipleObjects函数会,返回值介于WAIT_ABANDONED_0和WAIT_ABANDONED_0+N+1之间,
  N是指handles数组中元素个数
  5.但是被中断的线程保护的数据是无法修复的
  
"4.3.6 处理被舍弃的互斥器 }}}
"4.3.7死锁 {{{
    任何时候只要你锁住超过一个以上的同步对象,就有死锁的潜在危险
"4.3.7死锁 }}}
"4.3 Mutex }}}
"4.4Semaphores  {{{
"4.4.1 概念 {{{

semaphore是一个可以锁住n次,可以用几个线程不定,
1.最多锁定次数n是产生semaphore时指定的
2.如最大锁定次数是1,那就是一个mutex
3.semaphore适合保护连环的环状缓冲区
4.以环状缓冲区为例,semaphore以0为初值,任何线程都会停下来,
一旦有东西加到环状缓冲区,就以ReleaseSemaphore增加semaphore的值,
所有等待的线程继续运行
"4.4.1 概念 }}}
"CreateSemaphore {{{
HANDLE CreateSemaphore(
    LPSECURITY_ATTRIBUTES lpAttributes,
    LONG IInitialCount,
    LONG IMaximumCount,
    LPCTSTR lpName )

lpAttributes  安全属性,NULL用默认
IInitialCount semaphore的初值,大于或等于0,小于或等于IMaximumCount,
              这个参数的作用也是保证不会发生race condition的风险,
              使产生的初始值不会有被意外改写的可能
IMaximumCount semaphore的最大值
lpName  Semaphore的名称,任何线程都可以根据名称引用,
        NULL产生没有名字的semaphore
返回值:
    成功返回handle,否则传回NULL,
    名称已经存在,还是返回成功,传回ERROR_ALREADY_EXISTS

"CreateSemaphore }}}
"ReleaseSemaphore {{{
BOOL ReleaseSemaphore(
    HANDLE hSemaphore,
    LONG lReleaseCount,
    LPLONG lpPreviousCount
    );
hSemaphore      Semaphore的handle
lReleaseCount   要给Semaphore的增加的值,不可以是负值或0.
lpPreviousCount 传回semaphore原来的值

返回值:
成功true,否则返回false
注意:
  lppreviouscount传回的是瞬时的值,lReleaseCount+lppreviouscount不一定等于Semaphore
  的当前值
"ReleaseSemaphore }}}
"4.4Semaphores  }}}
"4.5 Event {{{
"4.5.1 Event事件 {{{
Event是核心对象,只有激发状态和未激发状态,且不受Wait...函数的副作用,
"4.5.1 Event事件 }}}
"Event的手动(Manual)和自动(Auto){{{
无论是手动还是自动类型的事件,中的恢复都是指的将自身事件设置为无信号状态.
手动复原的事件:
每次SetEvent都会唤醒所有等待中的线程,直至明确调用ResetEvent函数将其置为无符号状态.

自动复原的事件:
当一个自动复原的事件对象的状态被置为有信号状态时,该对象状态将一直保持有信号状态,直至第一个
等待到信号线程被释放;
系统将自动将此函数置为无符号状态.如果没有等待线程正在等待,事件对象的状态将保持有信号状态.
>>
如果多个线程一起等待同一个自动类型事件,就有线程死锁的危险,
因为多次设置为有信号的函数调用,并不会累加,因而会被一次线程释放而消耗光.
所以事件如果是多个线程同用就不应该是自动类型的.
<<
*************************************************************************************************
PulseEvent函数已经不应该在用于VS2012以后版本中的项目中了

*************************************************************************************************

*************************************************************************************************

*************************************************************************************************
PulseEvent函数已经不应该在用于VS2012以后版本中的项目中了
*************************************************************************************************
每次PulseEvent都会唤醒第一个等待到此信号的线程(然后又立刻进入等待状态)
*************************************************************************************************

*************************************************************************************************
PulseEvent函数已经不应该在用于VS2012以后版本中的项目中了,以下是很早版本的对PulseEvent函数的解释
*************************************************************************************************
自动复原的事件:event对象总是处于非激发状态,
1.使ResetEvent函数是没有意义的,因为Event本身就的ResetEvent执行后想要的结果.
2.SetEvent和PulseEvent会唤醒一个等待中的线程
3.调用SetEvent或PulseEvent函数时,没有线程正在等待,那么这个唤醒的请求被遗失,且不会被保留.
*************************************************************************************************

操作系统会强迫让等待中的线程有轮番更替的机会,如果操作系统没有实现这种公平性.会使某些线程不断获得执行机会,
而某些线程一直未获得执行机会,这种情况称为starvation(饥饿)
"}}}
"4.5.2 CreateEvent {{{
HANDLE CreateEvent(
    LPSECURITY_ATTRIBUTES lpEventAttributes,
    BOOL bManualReset,
    BOOL bInitialState,
    LPCTSTR lpName
    );

lpEventAttributes 安全属性,NULL默认
bManualReset  
              如果bManualReset==TRUE,那么必须用ResetEvent函数来手动将事件的状态复原到无信号状态.
              如果bManualReset==FALSE,当一个等待线程被释放以后,系统将会自动将事件状态复原为无信号状态.
bInitialState TRUE,初始值激发状态,FALSE,初始值未激发状态
lpName    Event对角的名字

返回值:
  成功,返回Event handle,GetLasetError传回0,
  指定的event对象名存在,返回Event handle,GetLaseError传回ERROR_ALREADY_EXISTS
  失败,返回NULL

"4.5.2 CreateEvent }}}
"SetEvent和ResetEvent,PulseEvent只是为了兼容早期的代码{{{
BOOL SetEvent(HANDLE hEvent);
    设置事件的状态为有标记.
备注:
    1.如果事件是手动的,
      1.1   唤释放所有等待线程.醒所有等待线程;
      1.2   此事件将保持有标记,直到调用ResetEvent才会恢复为无状态.
    2.如果事件是自动的,此事件将保持有标记,
      2.1   如果调用此函数时存在等待此event的时线程,该线程被释放,系统将设置事件的状态为无标记;
      2.2   如果没有线程在等待,则此事件将保持有标记,直到存在一个等待此event的线程.

BOOL WINAPI PulseEvent(_In_ HANDLE hEvent);
这个函数是不可靠的,不应该被使用.它的存在主要是为了向后兼容.

ResetEvent  把Event对象设置为非激发状态
"Event函数 }}}
"4.5 Event }}}
"4.6 原子操作函数 {{{

LONG InterlockedIncrement(
    LPLONG lpTarget );

lpTarget 指向的变量加1,然后与0比较,作为函数的返回值,并且整个操作是不可分割的

LONG InterlockedDecrement(
    LPLONG lpTarget );

lpTarget 指向的变量减1,然后与0比较,作为函数的返回值,并且整个操作是不可分割的

LONG InterlockedExchange(
    LPLONG lpTarget ,
    LONG lValue );

lpTarget 被替换的32位变量的指针
lValue  用于替换的新值

用新值替换旧值,返回旧值,并且操作是不可分割的原子操作

"4.6 原子操作函数 }}}
"第四章 线程同步 }}}
"第五章 不要让线程成为脱缰野马 {{{
"5.1 如何安全结束线程和进程 {{{
BOOL TerminateThread(
    HANLDE hThread,
    DWORD dwExitCode 
    );
hThread 要结束的线程
dwExitCode  收回线程代码
1.成功返回TRUE,失败传回FALSE
2.此函数不允许线程有清理自己的机会,
  1.堆栈没有释放,引起内存泄漏
  2.任何与此线程有附着的DLLS也没有机会,获得线程解除附着的能和
  3.如果线程正在进入一个Critical Section,该Critical Section将永远处于锁定状态
  4.如果线程正更新一个数据结构,那么这个数据结构的状态也是不可预测的
  5.此函数唯一可以相信的是,线程handle会将变成激发状态,并且会传回dwExitCode所指定的结束代码
  
结束线程或进程:
1.必须按次序进行,以避免发生race conditions
2.就好像拆除一栋建筑,必须首先确定,每一个人都安全离开了
"5.1 如何安全结束线程和进程 }}}
"5.2 线程的优先级 {{{

Win32优先仅是以数值表现的,并以:
进程的优先权类别(priority class),
线程的优先权层级(priority level),
和操作系统的动态提升(Dyanmic Boost)作为计算基准,
所有因素一起用0~31的数值,标识线程的优先级

优先级最高的线程,就是下一个将执行的线程
如果优先权类别和优先权层级都相同,那么就轮流执行,即所谓round robin调度方式

优先权类别(Priority Class)
  HIGH_PRIORITY_CLASS         13
  IDLE_PRIORITY_CLASS         4
  NORMAL_PRIORITY_CLASS       7or8
  REALTIME_PRIORITY_CLASS     24

1.大部程序使用NORMAL_PRIORITY_CLASS,
2.少数使用其它,例:Task Manager使用HIGH_PRIORITY_CLASS
3.优先权类别适用进程而非线程
4.SetPriorityClass和GetPriorityClass设置和获取其值

优先权层级(Priority Level)

THREAD_PRIORITY_HIGHEST         +2
THREAD_PRIORITY_ABOVE_NORMAL    +1
THREAD_PRIORITY_NORMAL          0
THREAD_PRIORITY_BELOW_NORMAL    -1
THREAD_PRIORITY_LOWEST          -2
THREAD_PRIORITY_IDLE            Set to 1
THREAD_PRIORITY_TIME_CRITICAL   Set to 15

BOOL SetThreadPriority(
    HANDLE hThread,
    int nPriority
    );
hThread     代表调整优先权的线程
nPriority   线程优先级数值

成功返回设置的数值,失败FALSE

int GetThreadPriority(
    HANDLE hThead 
    );
成功返回 THREAD_PRIORITY_...系列宏,失败THREAD_PRIORITY_ERROR_RETURN

动态提升(Dynamic Boost)
1.第一种提升,使拥有键盘焦点的程序优先权提升至+2,保持前台程序敏感度
2.第二种提升,属于同一进程的线程,用以反应用户的输入或磁盘的输入
3.最后一种提升可发生任何一个线程,当Wait...函数等待对象满足条件时,
线程优先权会获得动态提升
"5.2 线程的优先级 }}}
"5.3 线程的暂停和恢复 {{{
DWORD ResumeThread(
    HANDLE hThread 
    );
hThread 欲被再次执行的线程
成功,返回线程前一个挂起次数,失败,返回0XFFFFFFFF

DWORD SuspendThread(
    HANDLE hThread
    );
hThread 欲被挂起的线程
成功返回目前的挂起次数,失败,返回0XFFFFFFFF

注意:不要将占有Critcal Section的线程挂起

"5.3 线程的暂停和恢复 }}}
"第五章 不要让线程成为脱缰野马 }}}
"第六章 Overlapped I/O {{{
"6.1 Overlapped 概念 {{{

overlapped I/O :操作系统在传送数据完毕时会发送通知的I/O操作,可以使你在I/O进行过程中,
           去处理其它事物
含括:
激发的文件 handles
激发的event对象
异步过程调用(Asynchronous Procedure Calls,APCs)
I/O completion ports

I/O completion ports是唯一适用于同时维护多连接线路的高负载服务器的技术,
这样的架构特别适用利用SMP(多CPU系统)系统,产生的scalable功能的服务器
scalable  能随着硬件性能提升,提升应用程序效能的系统功能
"6.1 Overlapped 概念 }}}
"6.2 文件Overlapped I/O  {{{

HANDLE CreateFIle(
    LPCTSTR lpFileName,     //文件名
    DWORD dwDesiredAccess,  //存取模式
    DWORD dwShareMode,    //共享模式
    LPSECURITY_ATTRIBUTES lpSecurityAttributes, //安全属性结构指针
    DWORD dwCreationDispostion, //生成模式
    DWORD dwFlagsAndAttributes, //文件属性
    HANDLE hTemplateFile  //接收全部属性拷贝的handle
    );

dwCreationDispositionLong,下述常数之一:
CREATE_NEW 创建文件;如文件存在则会出错
CREATE_ALWAYS 创建文件,会改写前一个文件
OPEN_EXISTING 文件必须已经存在。由设备提出要求
OPEN_ALWAYS 如文件不存在则创建它
TRUNCATE_EXISTING 将现有文件缩短为零长度

其中dwflagsandattributes中可以,指定对此文件的访问是overlapped I/O,
1.可以同时读或写文件许多部分,并且每次访问都用同样的handle和同样的文件位置.
2.overlapped请求的执行次序保证,尤其是面对多磁盘或不同种类设备的时候.
3.overlapped I/O 是由ReadFile和WriteFile实现的, C runtime library 中的
  fgets,fprintf无法实现overlapped I/O.
  
BOOL ReadFile(
    HANDLE hFile,   //文件handle
    LPVOID lpBuffer,  //接收数据缓冲区
    DWORD nNumberOfBytesToRead, //要读取字节
    LPDWORD lpNumberOfBytesRead,  //接收实际读取字节
    LPOVERLAPPED lpOverlapped   //overlapped info结构指针
    );
BOOL WriteFile(
    HANDLE hFile, //文件handle
    LPVOID lpBuffer,  //要写入数据缓冲区
    DWORD nNumberOfBytesWrite,  //要写入字节数
    LPDWORD lpNumberOfBytesWritten, //接收实际写入字节数
    LPOVERLAPPED lpOverlapped //Overlapped info结构指针
    );

VOERLAPPED结构:

typedef struct _OVERLAPPED{
  DWORD Internal;
  DWORD IntermalHigh;
  DWORD Offset;
  DWORD OffsetHigh;
  HANDLE hEvent;
}OVERLAPPED,*LPOVERLAPPED;

internal  通常保留,当GetOverlappedResult返回FALSE并且GetlastError返回ERROR_IO_PENDING时,
          此域含一视系统而定的状态
IntermalHigh 通常保留,当GetOverlappedResult返回TRUE,此域含被传输数据的长度
Offset    文件访问的起始偏移位置,偏移位置以文件头基准,如果目标设备不支持文件位置,忽略
OffsetHigh  64位文件偏移位置,较高32位,目标如不支持文件位置,忽略
hEvent    一个手动(manual-reset)的event,当overlapped I/O 完成时激发,
          ReadFileEx和WriteFileEx会忽略这个栏位,可能用自定义的指针
          这个event必须是manual-reset(手动重置的),否则可能race rondition
          
由于OVERLAPPED的生命超越ReadFile和WriteFile函数,所以要把此结构放在安全的地方

等待OVERLAPPED的执行结果,在WaitForMultipleObjects等待的handle数组中加上文件的handle,
当文件操作完成,接到通知,调用GetOverlappedResult确定结果

BOOL GetOverlappedResult(
    HANDLE hFile,
    LPOVERLAPPED lpOverlapped,
    LPDWORD lpNumberOfBytesTransferred,
    BOOL bWait 
    );

HANDLE hFile    文件或设备handle
LPOVERLAPPED lpOverlapped OVERLAPPED结构指针
LPDWORD lpNumberOfBytesTransferred  接收实际传输字节数
BOOL bWait TRUE要等待操作完成,FALSE不等待

成功TRUE,失败FALSE,如果bWait为FALSE而overlapped没有完成,
GetLasstError返回ERROR_IO_INCOMPLETE.

如果系统或同一程序中,有太多缓冲区被锁定,系统会返回ERROR_INVALID_USER_BUFFER的错误,
表示此刻没有足够的资源来处理这个I/O请求.
"6.2 文件Overlapped I/O  }}}
"6.3 APC {{{

使用ReadFileEx和WriteFileEx可以通过指定callback回调函数,称为I/O completion routine
1.可以等待大于MAXIMUM_WAIT_OBJECTS个对象
2.不用再根据Wait...函数返回值,计算是哪一个handle被激发
3.当I/O操作完成,线程处于 alertable(alert 警报,警告)状态时,系统才会调用callback函数.
当线程因以下五函数处于等待状态,alertable状态被标记为TRUE.
SleepEx,
WaitForSingleObjectEx,
WaitForMutipleObjectsEx,
MsgWaitForMultipleObjectsEx,
SignalObjectAndWait

I/O completion routine 格式:

VOID WINAPI FileIOCompletionRoutine(
    DWORD dwErrorCode,
    DWORD dwNumberOfBytesTransferred,
    LPOVERLAPPED lpOverlapped 
    );

dwErrorCode 此参数以下值:0操作完成,ERROR_HANDLE_EOF操作已经到达文件尾端
dwnumberofbytestransferred 真正被传输的数据字节数
lpoverlapped OVERLAPPED结构指针,此结构由开启overlapped I/O 操作的函数提供
使用APCS时,OVERLAPPED结构中的Event域不需要一个Event Handle,最好的用途是,
首先配置一个描述数据来自哪里的结构,或是对数据需要进行一些什么操作,然后用
hEvent栏位指向此结构.
1.处理完所有APCs之后,WaitForMultipleObjects会返回WAIT_IO_COMPLETION
2.如需将C++成员函数当做 I/O completion routine函数,
  1.储存一个自定义结构指针,由此指针调用C++其它成员函数.
  2.只有static函数,才能成为I/O completion routine函数.
  3.由于static成员函数也是类的一部分,还是可以调用private成员函数.
    
在像Web服务器这样的环境中,几乎每一笔数据传输量都很小,使用overlapped I/O反而
会降低整体效率,一种合理的变化是,以少量的线程负责所有硬盘I/O,然后这些线程的
I/O请求,保持在一个队列中.

有两种情况,overlapped总是同步执行,甚至即使FILE_FLAG_NO_BUFFERING已经指定,
1.写入操作造成文件扩展.
2.读写一个压缩文件.
APCS不足:
1.好几个I/O api不支持APC,如listen()和WaitCommEvent().
2.只有发出overlapped请求的线程才能提供callback函数
"6.3 APC }}}
"6.4 I/O Completion Ports  {{{

I/O Completion ports:
管理一维线程如何为completed overlapped I/O requests服务,并且
尽量保持每一个CPU都同样忙碌.
1.不限制handles个数.
2.允许将一个线程的请求暂时保存下来,由另一个线程为它实际服务.
3.产生一个completion port的步骤.
    1.产生一个I/O completion port.
    2.让它和一个文件handle产生关联.
    3.产生一堆线程.
    4.让每个线程都在completion port上等待.
    5.开始文件handle发出overlapped I/O 请求.

1.产生一个I/O completion port.
    HANDLE CreateIoCompletionPort(
        HANDLE FileHandle,
        HANLDE ExistingCompletionPort,
        DWORD CompletionKey,
        DWORD NumberOfConcurrentThreads
        );

HANDLE FileHandle     文件或设备handle,INVALID_HANDLE_VALUE产生一个没有和任何文件关联的port
    HANLDE ExisingCompletionPort  如果此项指定,此FileHandle指向文件Handle追加到此port,
                          而不产生新handle,
                                NULL,产生新port
    DWORD CompletionKey   自定义和FileHandle有关的数值
    DWORD NumberOfConcurrentThreads   与此I/O completion port 有关的线程个数,0不限制线程个数

返回值
        成返回I/O completion port的handle,失败FALSE

1.任何文件必须先以FILE_FLAG_OVERLAPPED开启才能附加到I/O completion port上,
    2.如果已经附加,就不能再以ReadFileEx或WriteFileEx操作
    4.可以安全的关闭任何一个文件

2.与一个文件handle产生关联.
    1.通常先将FileHandle设为INVALID_HANDLE_VALUE,,产生一个port.
    2.为每一个欲附着的文件handle调用一次CreateIoCompletionPort.
3.产生一堆线程.
    1.用CreateThread,_beginthreadex,AfxBeginThread之一产生线程
    2.执行的线程+被阻塞的线程+在completion port上等待的线程=线程池所有线程的总数
    3.合理的线程数量是CPU个数的两部再加2.
    
4.在I/O Completion Port上等待

BOOL GetQueuedCompletionStatus(
        HANDLE CompletionPort,
        LPDWORD lpNumberOfbytesTransferred,
        LPDWORD lpCompletionKey,
        LPOVERLAPPED *lpOverlapped,
        DWORD dwMilliseconds
        );

HANDLE CompletionPort                   等待的completion port
LPDWORD lpNumberOfbytesTransferred      接收实际的传输字节数
LPDWORD lpCompletionKey                 接收由CreateCompletionPort所定义的key
LPOVERLAPPED *lpOverlapped              用一Overlapped结构指针,接收系统返回状态
DWORD dwMilliseconds                    等待最长时间,如果时间终了,lpOverlapped被设为NULL,函数返回FALSE

返回值:
    如果成功从队列取出completion packet,并且响应成功,函数返回TRUE,
    并填写 lpNumberOfBytesTransferred,lpCompletionke,lpoverlapped指向的变量.
    如completion packet已经取出,操作失败,函数返回FALSE,lpOverlapped指向失败的操作.
    函数失败,返回FALSE,lpOverlapped设为NULL.
    
    1.此函数的用相当WaitForSingleObject和GetOverlappedResult
    2.completion port上等待的线程,以先进后出的次序提供服务
    3.一个进行中的线程,调用GetQueuedCompletionStatus就可以取得下一个request.
    
5.发出Overlapped I/O 请求
1.能够背I/O completion port补获的I/O函数
    connectNamepipe
    DeviceIoControl
    LockFileEx
    ReadRile
    TransactNamePipe
    WaitCommEvent
    WriteFile
对与此I/O completion port关联的文件进行读,写,或其实任何操作,线程池中等待的线程将会
自己释放完成I/O请求.

如果读写文件但不希望得到I/O completion Port通告,可以在WriteFIle和ReadFile中使用这样
的OVERLAPPED结构:
hEvent位放一个合法的(manual-reset)event对象,然后把handle最低位设为1.

OVERLAPPED overlapp;
HANDLE hFile;
char buffer[128];
DWORD dwBytesWritten;

memset(&overlap,0,sizeof(OVERLAPPED));
overlap.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
overlap.hEvent=(HANDLE)((DWORD)overlap.hEvent|0x1);

WriteFile(hFile,buffer,128,&dwBytesWritten,&overlap);

当调用CreateIoCompletionPort(),将一个文件和一个completion port产生关联时,必须使用一个自定义的
结构,用以跟踪目前handle上什么东西被读写.

"6.4 I/O Completion Ports  }}}
"第六章 Overlapped I/O }}}
"第七章 数据一致性 {{{
"7.1 volatile {{{
volatile 变量是多线程环境下的,易变的,告诉编译器不要持有变量的临时拷贝
void WaitForKey(char *pch)
{
  while(&pch==0);
}

{
  $L27:
      mov eax,DWORD PTR _pch[ebp]
      movsx eax,BYTE PTR [eax]
      test eax,eax
      jne $L28
      jmp $L27
  $L28:
}
如果在多线程程序中,另一线程如果改变了数值,循环永远不会结束,被测试的值永远放在寄存器中.
void WaitForKey(volatile char *pch)
{
  while(*pch==0);
}

{
  mov eax,DWORD PTR _pch[esp-4]
  movsx al,BYTE PTR [eax]
$L84:
  test al,al
  je SHORT $L84
}

const volatile变量传给函数是合法的,如此函数不能改变变量的值,
变量的值却可以被另一个线程在任何时间改变.
"7.1 volatile }}}
"7.2 Referential Integrity {{{
SQL Transaction:
    一组必须同时完成的不可分开的改变.
当开始一笔transaction后,数据将客户的改变一一储存,直到transaction完成为止,
所有改变一次性放在数据库中,并且在transaction进行时,数据库确保不会有任何客户拿到transaction的一部分.

数据库中consistency,transaction,integrity的细微差别:

transaction:
    某人有两个户头,户A转100元户B,户头A必须减100,户头B加100.
    两个操作都完成,才称一项 transaction 事务完成.
    有一个操作无法完成,该项事务作废,所有操作都必须还原(roll back).

事务(transaction)通常是指必须在短时间内完成的操作.
    作业(batch)长时间的批次作业,一个批次作业可能含许多笔事务(transaction).
    
Data consistency:
    数据一致性(Data consistency)通常是代表transaction的完成与否.如果 transaction 未能顺利完成,
就是Data consistency出了问题,所有相关操作必须还原.

referential integrity(参照完整性):
    假设数据库中有两张表,
    一个表是部门表,内有三笔数据,代表三个部门101,102,103, 
    另一个表是职员表,其中一栏为部门代号,任何一个职员的部门代号必须是101,102,103三者之一.
    这样的关系称为参照完整性(referential integrity又称为 referential constrain).
    如果有职员数据中出现104部门代表,就是referential integrity出了问题.
"7.2 Referential Integrity }}}
"7.3 用排他锁保护数据 {{{
HFILE WINAPI OpenFile(
  __in          LPCSTR lpFileName,
  __out         LPOFSTRUCT lpReOpenBuff,
  __in          UINT uStyle
);
第三个参数 uStyle 包含OF_SHARE_EXCLUSIVE,确保线程以独占模式打开文件,在操作文件
不会其它文件打扰,从而保护文件数据的完整性.

1.如果多个线程都是对文件进行读操作,那么之间不会干扰,要做的是防止有线程对进行写操作.
  所以,任何线程如果要读取文件,应以OF_SHARE_DENY_WRITE开启该文件.
2.如果一个线程正在写数据,其他线程无论读或写,都应被阻止.
  所以,任何线程如果要写文件,应该以OF_SHARE_DENY_READ和OF_SHARE_DENY_WRITE开启该文件.
"7.3 用排他锁保护数据 }}}
"7.4 何时需要数据保护 {{{
1.在一个以上的线程中使用同一块数据,就必须保护它.
2.如果是基础类型(DWORD或更小),应把这声明为volatile,用Interlocked...系统原子操作函数修改.
3.如果有许多,使用已经了多用户和多线程的数据库管理程序.
4.或者使用专用的线程读写在线程共享的数据.
"7.4 何时需要数据保护 }}}
"7.5 锁定粒度 {{{
Coarse Granularity 使用简单,死锁风险极低,但容易产生效率瓶颈
Fine Granularity 很容易产生死锁,效率较高,
实际程序中fine locking和coarse locking可以组合使用.
"7.5 锁定粒度 }}}
"7.6 多线程程序的建议和警告{{{
1.fine locking可能成为调试的恶梦,因为可能会出现错综复杂的依存关系.
2.如果决定使用fine granularity locking请反死锁的解决方案放进程序代码中,以便
用于找出问题所在.
3.建议程序一开始尽量少用locks,然后,
  1.发现一些瓶颈时,再开始适量使用locks.
  2.必须慢慢地,一步一步地进行,并且有完整的测试.
  3.否则死锁一旦出现,就不容易找出问题,因为你一次做了太多改变.
"7.6 }}}
"第七章 数据一致性 }}}
"第八章 C runtime library {{{
"8.1 两版本C库 {{{

1.VC有两版本的C runtime library 单线程版和多线程版
2.只要以 _beginthreadex函数取代CreateThread,就可以在任何多线程中安全地调用任何C runtime函数
3.
/ML 单线程
/MT 多线程静态库
/MD 多线程动态库
/MLd Debug 单线程
/MTd Debug 多线程静态库
/MDd Debug 多线程动态库

"8.1 两版本C库 }}}
"8.2 _beginthreadex和_endthreadex {{{

uintptr_t _beginthread( 
   void( *start_address )( void * ),
   unsigned stack_size,
   void *arglist 
);
uintptr_t _beginthreadex( 
   void *security,
   unsigned stack_size,
   unsigned ( *start_address )( void * ),
   void *arglist,
   unsigned initflag,
   unsigned *thrdaddr 
);

成功返回handle,失败返回0,原因设定在errno和doserrno全局变量中.
绝对不要在 _beginthreadex启动的线程中,调用ExitThread,
这样C runtime library没有机会释放线程资源.

void _endthread( void );

void _endthreadex( 
   unsigned retval 
);

"8.2 _beginthreadex和_endthreadex }}}
"8.3 何时使用_beginthreadex {{{
如果要以C runtime library 写一个多线程程序,必须保用
1.多线程版本的C runtime library
2._beginthreadex()/_endthreadex()
3.C程序不调用任何C runtime  library几乎不可能,编译器常常带为调用

如果主线以外的任何线程进行以下任何操作,
就应该保用多线程版的C runtime library,并保用_beginthreadex()和_endthreadex():

1.在C程序中使用malloc()和free(),在C++程序中使用new和dlelte.
2.调用stdio.h或io.h中声明的任何函数.fopen(),open(),getchar(),write(),printf()等等.
  这些函数使用了共享数据结构及errno.
3.使用浮点变量或浮点运算函数.
4.调用任何一个使用了静态缓冲区的runtime函数,如asctime(),strtok(),或rand().

如果没有使用以上操作,那么单线程版的C runtime library和CreateThread仍然是安全的.
"8.3 何时使用_beginthreadex }}}
"8.4 避免保用stdio.h {{{
1.用_wsprintfA()和_wsprintfW()取代sprintf()
2.用GetStdHandle()获取stdin,stdout,stderr,
  供给CreateFile,ReadFile,WriteFile,CloseHandle作为参数.

HANDLE WINAPI GetStdHandle(
  __in          DWORD nStdHandle
);
DWORD nStdHandle:
STD_INPUT_HANDLE 
STD_OUTPUT_HANDLE 
STD_ERROR_HANDLE 
3.使用Console API控制屏幕
"8.4 避免保用stdio.h }}}
"8.5 结束进程 {{{
对于以_beginthread()或_beginthreadex()来产生新线程的程序,
  1.应以exit()函数或从main返回函数,都会自动清理C runtime library,最后调用ExitProcess().
  2.如果需要等待线程完成重要工作再退出,可以使用Wait...系列函数等待后再退出.
  3.极端糟糕的情况下,可以在任何调用abort().
"8.5 结束进程 }}}
"8.6 避免使用_beginthread和CreateThread {{{
_beginthread
1.无法将线程产生于挂起状态,以便调整以及数据的初始化.
2.此函数产生出线程,第一件事就是关闭自己的handle,因此返回的handle可能当时是不可用的,
  因为尝试使用由此函数传回的handle,无可避免导致race rondition.
3._beginthread()产生出来的线程调用GetExitCodeThread()是没有意义的,认任何一个_beginthread
产生出来的线程传回一个结束代码同样无意义.

CreateThread
使用此函数容易造成内存泄露

"8.6 避免使用_beginthread和CreateThread }}}

"第八章 C runtime library }}}
"第九章 使用C++ {{{
1.线程函数必须符合__cdcel或WINAPI调用约用.
2.类的非静态成员函数无法成为线程函数,只有类静态成员函数或C函数.
  因为类的非静态成员函数有隐含的this指针作为参数.
3.但使用类的静态成员函数可以直接处理类的private或protected访问权根.
4.使用C++类实现同步机制可以实现stack unwind堆栈并清除所有变量.
  stack unwind 当一个异常发生时,将堆栈中的所有对象自动执行析构.
"第九章 使用C++ }}}
"第十章 MFC中的线程 {{{
"10.1 产生worker线程 {{{

如果要在MFC程序中产生一个线程,该线程将调用MFC函数或使用MFC的任何数据,那这个线程
必须以AfxBeginThread()或CWinThread::CreateThead()来产生这些线程.

产生worker线程的AfxBeginThread:
CWinThread* AfxBeginThread(
   AFX_THREADPROC pfnThreadProc,
   LPVOID pParam,
   int nPriority = THREAD_PRIORITY_NORMAL,
   UINT nStackSize = 0,
   DWORD dwCreateFlags = 0,
   LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL 
);

CWinThread将如下函数和变量封装类中,
CWinThread::Resumethread()
CWinThread::SetThreadPriority()
CWinThread::m_hThread   线程handle
CWinThread::m_nThreadID 线程ID
这个成员变量CWinThread::m_bAutoDelete,可以阻止CWinThread对象被自动删除,
必须以挂起方式产生线程,才能保证设定这个变量而不引起race rondition.
无论此变量被设定为何值,CWinThread在析构函数中都调用CloseHandle().

如果设置m_bAutoDelete = FALSE;则需要你在线程结束后,要手动delete掉 CWinThread*对象;
如果没设置m_bAutoDelete的话,则需要保证线程中始终在任何情况下都能执行 return 0;语句返回,
因为只有这样才可以将在线程中使用return返回所有资源(包括c++对象)都可以得到正确的释放.

AfxBeginThread()将CWinThread的使用做了封装的函数.
可以根据自己的需要,封装CWindThread的派生类,对启动的线程得到更好的控制.

CWinThread::m_pThreadParams 线程函数的起始参数.
"10.1 产生worker线程 }}}
"10.2 产生UI线程 {{{
产生UI线程的AfxBeginThread:
CWinThread* AfxBeginThread(
   CRuntimeClass* pThreadClass,
   int nPriority = THREAD_PRIORITY_NORMAL,
   UINT nStackSize = 0,
   DWORD dwCreateFlags = 0,
   LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL 
);

CRuntimeClass* pThreadClass  自定义的CWinThread派生类对象的指针.
可以选择修改以下虚函数自定义自己的派生类
InitInstance
ExitInstance
OnIdle
PreTranslateMessage
IsIdleMessage
ProcessWndProcException
ProcessMessageFilter
Run

默认只改写InitInstance就会开启消息循环.
MFC中每个程序都有的CWinApp类也是派生自CWinThread.
"10.2 产生UI线程 }}}
"10.3 MFC对象 {{{
    MFC对象和Win32 handles之间的映射在线程局部存储表TLS中,MFC线程之间无法传递对象和
对象的指针,这些MFC对象包括(但不限于)CWnd,CDC,CPen,CBrush,CFont,CBitmap,CPalette等等.

于多线程有关的核心对象,MFC都做了封装
  Critical Section      CCriticalSection
  Event                 CEvent
  Semaphore             CSemaphore
  mutex                 Cmutex
"10.3 MFC对象 }}}
"第十章 MFC中的线程 }}}
"第十一章 GDI与窗口管理 {{{
Win32中,每个线程都有自己专属的消息队列,并不是每个窗口都有自己的消息队列.因为每
个线程能够产生很多窗口,如果一个线程停止响应,或者它忙于一段耗时的计算工作,那么
由它差生的窗口统统都会停止响应,但系统中的其他窗口还是继续正常的运作.

所有传递给某一窗口的消息,将由产生该窗口之线程负责处理.

对窗口所做的任何一个事情基本上都由该窗口的窗口函数处理,并因此被产生该窗口的线程
处理.

SendMessage()

同一线程:直接调用窗口函数

不同线程:切换到新线程并调用窗口函数 ,在该函数返回之前,SendMessage不会返回(所
以会发生意Context Switch,比一般函数调用费时间)

PostMessage()

同一线程:把消息放入消息队列立即返回

不同线程:立即返回,消息放入另一线程的消息队列中

ReplyMessage
该函数用于应答由函数SendMessage发送的消息,不返回控制给调用SendMessage的函数。

----------------------------------------------------

所以如果各个窗口都有自己的线程,那么在处理各个窗口的响应时可能要发生很多次的
context switch ,效率低下,所以不能有自己的线程.

如果一个线程调用了SendMessage后,当前线程就会等待其返回,类似于sleep状态,但是还
是可以处理 外界对其窗口的sendMessage操作.

如果某个线程正在 处理由其他线程Send过来的消息,可以在该线程中调用IsSendMessage,
会获得TRUE.为了让调用端线程能够继续工作,我们可以调用ReplyMessage.

为了防止线程因为SendMessage导致的死锁(永远醒不过来) ,Win32提供2个函数.第一个
是SendMessageTimeout,允许你指定一个时间,时间终了了一定会返回.第二个是
SendMessageCallback.这个函数会立刻返回.

------------------------------------------------

欲在一个MDI程序中有效率的使用多个线程,建议是以一个线程处理所有的用户输入以及用
户界面的管理.用其他线程做一些后台的耗时操作.不管怎么做,主线程应该始终有回应.

线程之间的通讯

线程通常需要传递数据给另外一个线程,worker线程告诉别人他的工作做完了,GUI线程可
以需要交给worker线程一个新工作.PostThreadMessage 类似于PostMessage,不过他的参数
之一不是窗口的handle,而是线程ID,当然,接收端必须有消息队列.

BOOL PostThreadMessage(
        DWORD idThread,  //线程ID,可以有GetCurrentThreadID或CreateThread获得
        UINT Msg,
        WPARAM wParam, 
        LPARAM lParam
        );

返回值:如果成功的Post出去,返回TRUE.

以消息作为通讯方式,比起标准技术如全局变量等,有很大好处,比如目标线程很忙碌,则
负责post的那个线程不会停滞下来.如果对象是同一进程的线程,可以自定义消息,如
WM_APP+0X100等,并配置一块结构,防止你想要传递的数据.然后把结构指针当做lParam,
接收端在处理完消息后负责释放内存.

锁住GDI对象 
  GDI对象被每个进程拥有,而被每个线程锁定.如果一个线程正在使用一个GDI对象,该对象
将被锁定,其他线程没有办法在使用它.

"第十一章 GDI与窗口管理 }}}
"第十二章 线程间的通信 {{{

robustness 健壮性
overhead 负担
在不同线程间搬移数据.
方法一:用于在线程间传递数据的信息WM_COPYDATA.

"第十二章 线程间的通信 }}}
"多线程API {{{

==========================================================================
GetMessage:
    从系统获取消息,从系统中移除,属于阻塞函数。
    当系统无消息时,GetMessage会等待下一条消息。
PeekMesssge:
    查看系统消息,可以不将消息从系统中移除,是非阻塞函数;
    当系统无消息时,返回FALSE,继续执行后续代码。

BOOL PeekMessage(LPMSG lpMsg,
    HWND hWnd,
    UINT wMsgFilterMin,
    UINT wMsgFilterMax,
    UINT wRemoveMsg
);

If wMsgFilterMin and wMsgFilterMax are both zero, PeekMessage returns 
all available messages (that is, no range filtering is performed).

wMsgFilterMax
lpMsg         接收消息信息的MSG结构指针。
hWnd           其消息被检查的窗口句柄。
wMsgFilterMin 指定被检查的消息范围里的第一个消息。
wMsgFilterMax 指定被检查的消息范围里的最后一个消息。
wRemoveMsg     确定消息如何被处理。此参数可取下列值之一[1]:

PM_NOREMOVE     处理后,消息不从队列里除掉。
PM_REMOVE       处理后,消息从队列里除掉。
PM_NOYIELD      不释放等待调用程序空闲的线程。

可将PM_NOYIELD随意组合到PM_NOREMOVE或PM_REMOVE。

如果消息可得到,返回非零值;如果没有消息可得到,返回值是零。
==========================================================================
CreateEvent(
        /*LPSECURITY_ATTRIBUTES 返回句柄是否子进程继承,NULL不继承*/,
        /*true用ResetEvent设为无信号,false等待线程结束,系统设置无信号/,
        /*BOOL  true事件有信号,false事件无信号*/,
        /*LPCTSTR 对象名称,大小写敏感,长度MAX_PATH*/
        );
==========================================================================
SetEvent(HANDLE );
        将HANDLE核心对象设置为有信号状态
==========================================================================

"多线程API }}}
"多线程单词 {{{

context switch batch language     批处理语言  
TSR (Terminated and StayResident) 常驻任务  
task switching                    工作切换 
overhead                          额外负担 
SMP (Symmetric Multi-Processing)
corruption                        数据破坏    
resource leaks                    资源泄漏
start up                          启动
cleans up                         清理
process                           进程
thread                            线程
file                              文件
event                             事件
semaphore                         信号量
mutexe                            互斥器
named pipe                        命名管道
anonymous pipe                    匿名管道
overlpped                         重叠
bound                             绑定
primary thread
GUI (Graphic User Interface)
synchronouts                      同步    SendMessage是同步行为
asynchronous                      异步    PostMessage是异步行为
critical section                  邻界区
Pulse                             拍动,跳动,振动
Manual                            手动
Automatic                         自动
context                           上下文
switch                            切换,交换
priority                          优先级
priority class                    优先权类别
priority level                    优先层级
Dynamic Boost                     动态提升
round robin                       调度方式
scalable                          可伸缩
RAS( Remote Access Servie )       远程访问服务
APC(Asynchronous Procedure Calls) 异步过程调用
Referential                       引用 
Integrity                         完整
transaction                       事务
consistency                       一致性
transaction                       交易,执行,办理
integrity                         正直,诚实,完整,完全
batch                             一组,一起,一捆
roll back                         还原
exclusive                         独占的,排他的,唯一的
deny                              否定,拒绝
coarse                            粗糙的,粗俗的,下等的
fine                              细微的,精巧的,很好的
granularity                       间隔尺度,粒度
unwind                            展开,延伸
robustness                        健壮性
overhead                          负担
"多线程单词 }}}
"应该使用什么函数创建线程CreateThread,_beginthreadex,还是AfxBeginThread{{{
当使用CRT时(基本上所有的程序都使用CRT),请尽量使用_beginthreadex()/_endthreadex()这组函数来创建线程.
在MFC中,是AfxBeginThread()和AfxEndThread(),它是MFC层面的线程包装函数.
当我们使用CRT和MFC类库时,它们会维护线程与CRT和MFC相关的结构的使用和资源申请和释放等相应的必要的操作.
尽量使用它提供的线程包装函数以保证程序运行正确.
"}}}

《win32多线程程序设计》学习笔记相关推荐

  1. 第二行代码学习笔记——第六章:数据储存全方案——详解持久化技术

    本章要点 任何一个应用程序,总是不停的和数据打交道. 瞬时数据:指储存在内存当中,有可能因为程序关闭或其他原因导致内存被回收而丢失的数据. 数据持久化技术,为了解决关键性数据的丢失. 6.1 持久化技 ...

  2. 第一行代码学习笔记第二章——探究活动

    知识点目录 2.1 活动是什么 2.2 活动的基本用法 2.2.1 手动创建活动 2.2.2 创建和加载布局 2.2.3 在AndroidManifest文件中注册 2.2.4 在活动中使用Toast ...

  3. 第一行代码学习笔记第八章——运用手机多媒体

    知识点目录 8.1 将程序运行到手机上 8.2 使用通知 * 8.2.1 通知的基本使用 * 8.2.2 通知的进阶技巧 * 8.2.3 通知的高级功能 8.3 调用摄像头和相册 * 8.3.1 调用 ...

  4. 第一行代码学习笔记第六章——详解持久化技术

    知识点目录 6.1 持久化技术简介 6.2 文件存储 * 6.2.1 将数据存储到文件中 * 6.2.2 从文件中读取数据 6.3 SharedPreferences存储 * 6.3.1 将数据存储到 ...

  5. 第一行代码学习笔记第三章——UI开发的点点滴滴

    知识点目录 3.1 如何编写程序界面 3.2 常用控件的使用方法 * 3.2.1 TextView * 3.2.2 Button * 3.2.3 EditText * 3.2.4 ImageView ...

  6. 第一行代码学习笔记第十章——探究服务

    知识点目录 10.1 服务是什么 10.2 Android多线程编程 * 10.2.1 线程的基本用法 * 10.2.2 在子线程中更新UI * 10.2.3 解析异步消息处理机制 * 10.2.4 ...

  7. 第一行代码学习笔记第七章——探究内容提供器

    知识点目录 7.1 内容提供器简介 7.2 运行权限 * 7.2.1 Android权限机制详解 * 7.2.2 在程序运行时申请权限 7.3 访问其他程序中的数据 * 7.3.1 ContentRe ...

  8. 第一行代码学习笔记第五章——详解广播机制

    知识点目录 5.1 广播机制 5.2 接收系统广播 * 5.2.1 动态注册监听网络变化 * 5.2.2 静态注册实现开机广播 5.3 发送自定义广播 * 5.3.1 发送标准广播 * 5.3.2 发 ...

  9. 第一行代码学习笔记第九章——使用网络技术

    知识点目录 9.1 WebView的用法 9.2 使用HTTP协议访问网络 * 9.2.1 使用HttpURLConnection * 9.2.2 使用OkHttp 9.3 解析XML格式数据 * 9 ...

  10. 安卓教程----第一行代码学习笔记

    安卓概述 系统架构 Linux内核层,还包括各种底层驱动,如相机驱动.电源驱动等 系统运行库层,包含一些c/c++的库,如浏览器内核webkit.SQLlite.3D绘图openGL.用于java运行 ...

最新文章

  1. sourcetree 卡顿_Android卡顿性能监测方案对比
  2. Java基础部分自测题(持续更新)
  3. [react] 你有使用过loadable组件吗?它帮我们解决了什么问题?
  4. LeetCode 1971. Find if Path Exists in Graph(图的遍历)
  5. Qt文档阅读笔记-QNetworkProxy::ProxyType解析(Qt设置Fiddler代理)
  6. Java Set接口详细讲解 TreeSet的定制排序和自然排序
  7. [LinuxVim]基础01
  8. Entity Framework 5自动生成ObjectContext或者DbContext的设置
  9. Hibernate中hbm.xml文件的inverse、cascade、fetch、outer-join、lazy
  10. 【vue开发问题-解决方法】(一)在style中设置background-image时路径问题
  11. 项目管理的前路怎么样?PMP证书作用如何?
  12. java学习软件_刚学习java,用哪些学习软件比较好?
  13. 一张图概括淘宝直播背后的前端技术 | 赠送多媒体前端手册
  14. 丢失MSVCR71.dll问题解决
  15. ftp文件服务器编码,ftp服务器字符编码
  16. CDN是什么?以及CDN的原理
  17. 【JZOJ 省选模拟】死星(deathstar )
  18. 使用结构方程模型需要知道的那些事(理论篇)
  19. lms算法的verilog实现_基于FPGA和LMS算法的系统建模
  20. Bringing up interface eth0: Device eth0 does not seem to be present, delaying initialization.

热门文章

  1. html+css+js 填写表单实现下一步上一步操作
  2. 朋友圈炫富背后的微信营销套路
  3. HT1621测试程序
  4. web网页版苹果计算器(HTML、CSS、JavaScript实现)
  5. VS2010WriteString函数不能输出汉字
  6. 大数据 为什么是贵州
  7. 2014年终总结——-我的匆匆这一年(面试,毕业季,工作)
  8. 【工作经验】实践!能拍近景的全景立体相机
  9. 从程序员到项目经理-转
  10. system.dll,Nskhelper2.sys,oapejg.sys,991b0345.dat,NsPass0.sys等1