张运涛

c++

   :: 首页 :: 联系 :: 聚合  :: 管理

常用链接

留言簿(4)

搜索

  •  

最新评论

 

上路吧,线程
/*
进程就是一大堆对象的拥有的集合,即进程拥有对象,进程可以拥有内存(memory context) ,可以拥有线程,可以拥有file handles,
可以拥有一大堆的dll模块。
在Win32中,handle只在诞生地才有意义,这是一种安全警戒,避免一个进程危及另一个进程资源。因次它成了多进程设计的最大
难题。
Context Switch :要切换threads, os 应先切换thread所隶属的内存,然后恢复该thread放在context结构中的寄存器的值,整个过程叫
context switch.
Race Conditions :在并发式多任务系统中,控制权被强行转移,也因此两个线程之间的执行次序变得不可预测,这不可预期性造成了所谓
的race conditions.
Atomic Operations: 一个操作,如果能不受interrupt地完成,我们称之为atomic operation.
*/

线程的第一次接触
--------------------------------------------------------------------------------
//产生一个线程。创建成功返回handle,被称为核心对象。否则返回FLASE。 可以调用GetLastError()获知原因

//返回线程句柄 
HANDLE hThrd = CreateThread(NULL,
                            
0,                              //线程堆栈大小。默认1MB
                            ThreadFunc,          //线程起始地址。函数指针;DWORD WINAPI ThreadProc( [in] LPVOID lpParameter);
                            (LPVOID)i,              //线程函数参数
                            0,                             //允许产生一个暂时挂起的线程。默认“立即开始执行” ;CREATE_SUSPENDED 
                            &threadId );           //新线程的ID会被传回这里。类型:DWORD
--------------------------------------------------------------------------------
/*
核心对象是系统的一种资源,系统对象一旦产生,任何应用程序都可以开启并使用该对象,如GDI 对象等,系统给予核心对象一个计数值
(usage count)作为管理之用,核心对象包括下列各种: event ,mutex,semaphore,file,file-mapping,process,thread
释放核心对象:CloseHandle();
*/

//成功返回TRUE,失败返回FALSE
BOOL CloseHandle(HANDLE hObject);
--------------------------------------------------------------------------------
/*
FAQ:为什么可以在不结束线程的情况下关闭其handle?
答:“线程核心对象”引用到的那个线程也会令核心对象开启,因此,线程对象的默认引用计数为2,用CloseHandle()后,减1,线程结束时,引用
计数再减1,这时对象才被清楚啊!想起COM了..
*/

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

//线程结束代码(exit code)。判断线程是否结束

//函数执行成功则返回TRUE,失败FALSE。线程的结束代码放在lpExitCode参数中,STILL_ACTIVE代表线程尚未结束。
BOOL GetExitCodeThread(
                       HANDLE hThread,                      
//由CreateThread()传回的线程handle
                       LPDWORD lpExitCode );           //指向一个DWORD,用以接收结束代码(exit code)。
--------------------------------------------------------------------------------

//结束一个线程。在线程函数内使用,相当于C runtime library中的exit()函数

VOID ExitThread( DWORD dwExitCode );               
//指定此线程之结束代码

--------------------------------------------------------------------------------
/*
主线程:程序启动后就执行的那个线程。
职责:1.必须负责GUI程序中的主消息循环。    
2.这一线程结束(除ExitThread()) 会使程序中的所有线程都被迫结束,其他线程有可能没机会清理工作.
在主线程中调用 ExitThread()。会导致主线程结束 而其他线程继续存在。这么做会跳过runtime library中的清理(cleanup)函数,
因而没有将已开启的文件清理掉。不推荐。
*/

--------------------------------------------------------------------------------
/*
Win32说明文件一再强调线程分为GUI线程和worker线程两种。GUI线程负责建造窗口以及处理主消息循环。worker负责执行纯粹运算工作
,如重新计算或重新编页等等,它们会导致主线程的消息队列失去反应。一般而言,GUI线程绝对不会去做那些不能够马上完成的工作。
GUI线程:拥有消息队列的线程。任何一个特定窗口的消息总是被产生这一窗口的线程抓到并处理。所有对此窗口的改变也都应该由该线程完成。
worker线程不能产生窗口、对话框、消息框,或任何其他与UI有关的东西。否则就会有一个消息队列产生出来并附着到此线程身上,变成GUI线程。
*/

--------------------------------------------------------------------------------
快跑与等待
--------------------------------------------------------------------------------
//等待一个核心对象变成激发态,才继续往下执行。

//此函数成功有三个因素:
//1. 等待的目标(核心对象)变成激发状态。 返回 WAIT_OBJECT_0 。
//2. 核心对象变成激发状态之前,等待时间终了。返回 WAIT_TIMEOUT 。
//3. 如果一个拥有 mutex (互斥器)的线程结束前没有释放 mutex,则返回 WAIT_ABANDONED 。 
//如果函数失败,则传回 WAIT_FAILED。调用 GetLaseError()。
DWORD WaitForSingleObject(
                          HANDLE hHandle,                      
//等待对象的 handle (代表一个核心对象)。
                          DWORD dwMilliseconds );       //等待的最长时间。时间终了,即使 handle 尚未成为激发状态,此函数还是要返回。
//  0(代表立即返回),也可以 INFINITE 代表无穷等待
/*备注:
设定 time-out 为0,使你能够检查 handle 的状态并立即返回,如果核心对象为激发态,那么这个函数会成功并传回 WAIT_OBJECT_0。
否则,函数立即返回并传回 WAIT_TIMEOUT 。
*/

--------------------------------------------------------------------------------
//等待多个进程

//返回值:
//1. 如果因为时间终了而返回,则返回值为 WAIT_TIMEOUT
//2. 如果 bWaitAll 是 TRUE,返回值将是 WAIT_OBJECT_0
//3. 如果 bWaitAll 是 FALSE,返回值减去 WAIT_OBJECT_0,就表示数组中哪一个 handle 被激发了。
//4. 如果你等待的对象中有任何 mutexes ,那么返回值可能从 WAIT_ABANDONED_0 到 WAIT_ABANDONED_0 + nCount -1。
//5. 如果函数失败,传回 WAIT_FAILED。GetLastError()查看
DWORD WaitForMultipleObjects(
                             DWORD nCount,                
// lpHandles 中元素个数。最大容量是 MAXIMUM_WAIT_OBJECTS
                             CONST HANDLE *lpHandles,    // 指向由对象 handles 所组成的数组。这些 handles 不需要为相同的类型
                             BOOL bWaitAll,                  // TRUE,表示所有 handles 都必须激发,此函数才返回。 否则,有一个激发就返回
                             DWORD dwMilliseconds );         // 该时间终了,函数就返回。0,用于测试。INFINITE,表示无穷
--------------------------------------------------------------------------------
/*
FAQ:什么是核心对象的激发态?
核心对象激发状态的意义。
Thread /Process :进程/线程结束时,变成激发态。正在运行时,为非激发态。
Mutex :若mutex没有被任何一个线程所拥有,它处于激发态,一旦一个等待mutex的函数返回了,mutex自动置为非激发态.
Semaphore :同mutex,但它有一个计数器,可以控制其拥有者的个数,当计数器大于0时,处于激发态。
Event :状态受应用程序自己控制。
*/

--------------------------------------------------------------------------------
在一个GUI程序中等待: 在“对象被激发”或“消息到达队列”时被唤醒而返回

//和 WaitForMultipleObjects 比较,返回值多了“消息到达队列”,返回值是 WAIT_OBJECT_0 + nCount
DWORD MsgWaitForMultipleObjects(
                                DWORD nCount,
                                LPHANDLE pHandles,
                                BOOL fWaitAll,
                                DWORD dwMilliseconds,
                                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
*/

WaitForMultipleObjects() 不允许 handles 数组中有缝隙产生。即参数中 handles 中的每个核心对象都不能为空。
--------------------------------------------------------------------------------
线程同步
--------------------------------------------------------------------------------
一.   临界区法。
在Win32程序中你可以为每一个需要保护的资源声明一个CRITICAL_SECTION 类型变量,让同一时间内只有一个线程进入critical section.
critical section 并不是核心对象,没有所谓的handle,它存在于进程的内存空间中,你应该做的是将一个类型为CRITICAL_SECTION的局部
变量初始化.
VOID InitializeCriticalSection(    LPCRITICAL_SECTION lpCriticalSection 
/*批向将被初始化的CRITICAL_SECTION变量*/);
一旦一个临界区被初始化,线程就可以进入其中:
VOID EnterCriticalSection (    LPCRITICAL_SECTION lpCriticalSection 
/*批向将被初始化的CRITICAL_SECTION变量*/);
离开临界区:
VOID LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection 
/*批向将被初始化的CRITICAL_SECTION变量*/);
清除临界区:
VOID DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection 
/*批向将被初始化的CRITICAL_SECTION变量*/);
--------------------------------------------------------------------------------
二.    Mutexes(互斥器)
            功能同critical section 相同,但它是一个核心对象,可以跨进程使用,且等待一个mutex时,你可以指定“结束等待的时间”。但锁住一个未被拥有
            的critical section 比它少百倍时间。
            
1.产生一个互斥器
            HANDLE CreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes,
            BOOL bInitialOwner,     
//如果你想吊用CreateMutex的进程拥有该互斥对象的所有权,则为true;
            LPCTSTR lpName        //mutex的名字,不含‘\’的字符串即可。
            );
返回一个handle,若不用了,要CloseHandle,核心对象都要这样啊,像com,呵。
[
2].打开一个mutex 
用OpenMutex(),并指定mutex的名字。
3.锁住mutex,用WaitForSignalObject即可。
4.释放mutex
BOOL ReleaseMutex( HANDLE hMutex);
--------------------------------------------------------------------------------
三,信号量Semaphores
semaphoressk 可以被锁n次,n通常被设计来代表“可以锁住一份资源”的线程下数。
1.产生Semaphores
HANDLE CreateSemaphore(LPSECURITY_ATTRIBUTES lpMutexAttributes,
                       LONG lInitialCount,    
//semaphore的初始值。
                       LONG lMaximumCount,//semaphore的最大值。
                       LPCTSTR lpName//seamphore的名字。
                       );
2.获得锁定(略,semaphores是核心对象啊,狗熊咋死的?)
3.释放semaphores
HANDLE ReleaseSemaphores(
                         HANDLE hSemaphore,
                         LONG lReleaseCount,
//Semaphore现值的增额,大于等于1.
                         [out] LPLONG lpPreviousCount//藉此传回semaphore的现值。
                         );
--------------------------------------------------------------------------------
四。事件Event Objects
可以完全控制一个event对象做什么,什么时候去做,不受Wait函数的影响。
1.产生一个event对象。
HANDLE CreateEvent(
                   LPSECURITY_ATTRIBUTES lpMutexAttributes,
                   BOOL bManualReset,
                   BOOL bInitialState,
                   LPCTSTR lpName,
                   )
                   bManualReset 
//用于指明该事件是个自动重置的事件还是一个人工重置的事件.
                   
//自动重置事件将在变成激发态之后,自动重置为非激发状态。唤醒一个线程.
                   
//手动重置事件将在变成激发态之后,不自动重置为非激发状态。唤醒全部等待的线程。
                   bInitialState//指定初始状态,
                   
//true,为激发态,即线程执行。
                   
//false,非激发态,暂停态.
                   2.控制函数
                   SetEvent();
//把event置为激发态
ResetEvent();//把event置为非激发态.
PulseEvent();//执行一次。
//若为手动事件时,将event置激发态,唤醒所有等待的线程,然后置为非激发态。
// 自动事件的话,,将event置激发态,唤醒一个等待的线程,然后置为非激发态。
示例:
// 全局句柄

HANDLE g_hEvent;

int WINAPI WinMain(

{
    
//创建一个人工重置的事件对象
    g_hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    
//产生三个线程
    HANDLE hThread[3];
    DWORD dwThreadID;
    hThread[
0= _beginthreadex(NULL, 0, WordCount, NULL, 0&dwThreadID); //WordCount thread
    hThread[1= _beginthreadex(NULL, 0, SpellCheck, NULL, 0&dwThreadID);//SpellCheck thread
    hThread[2= _beginthreadex(NULL, 0, GrammarCheck, NULL, 0&dwThreadID);//GrammarCheck thread
    OpenFileAndReadContentsIntoMemory();
    
//通知等待的线程,告诉他们,我的工作做完了,你们可以开始执行了
    SetEvent(g_hEvent);
}

--------------------------------------------------------------------------------
不要让线程成为脱缰野马
--------------------------------------------------------------------------------
/*
如何强制结束一个线程?查查有关线程的API函数,立刻便找到一个结束线程的函数―――――TerminateThread ( ) ; 用它吗?
噢!不要使用它。被TerminateThread ( )强制结束的线程可能会有造成以下后果:
1、  没有机会在自己结束前释放自己所使用的资源
2、  可能引起内存泄漏
3、  如果这个线程被结束时处于一个critical section之中,那么该critical section将因此永远处于锁定状态。
*/

--------------------------------------------------------------------------------
/*
那么如何强制结束一个线程?
Win32核准在程序代码中设立一个标记,利用其值来要求线程结束自己。
我们的做法是,利用一个手动重置event对象,Worker线程可以检查该event的状态以做决定。
在线程函数中,我们可以用WaitForSignalObject(hEvent,0)来检查event的状态。
*/

--------------------------------------------------------------------------------
//优先权类别
/*
“优先权类别”是进程的属性之一,这个属性可以表现出这一进程和其他进程比较之下的重要性。
priority class                     base priority(0-31)
HIGHT_PRRORITY_CLASS         13
IDLE_PRRORITY_CLASS             4
NORMAL_PRRORITY_CLASS      7/8
REALTIME_PRRORITY_CLASS    24
*/

//线程优先权
/*
线程的优先权层级(Priority Level)是对进程优先权的一个修改,使你能够调整同一个进程内的各线程的相对重要性。
Priority Level                            value
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 hTread, 
int nPriority) ;
int GetThreadPriority(HANDLE hThread);
--------------------------------------------------------------------------------
线程的唤醒与挂起

DWORD ResumeThread( HANDLE hThread);
//若函数成功,则传回线程的前一个挂起的次数,如果失败,则传回0xfffffff
DWORD SuspendThread(HANDLE hThread);
//若函数成功,则传回线程的目前挂起的次数,如果失败,则传回0xfffffff


posted on 2009-10-23 08:59 张运涛 阅读(597) 评论(0)  编辑 收藏 引用 所属分类: 多线程编程

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理