您的位置:首页 > 其它

多线程一

2013-10-21 13:13 253 查看
一些基础知识

进程包含内存和资源。资源包括内核对象,用户资源,以及GDI资源。

内存主要包括:代码,数据(全局变量和静态变量,以及动态分配的控件),以及堆栈。

在同一个进程中的线程大家共用内存以及资源。

context switch(CONTEXT结构):用于线程切换场景保存。

race conditions(竞争条件)

多线程无法预期,执行持续无法保证。

 

1.内核对象

        内核对象可以拥有多个拥有者,甚至可以是跨进程的。内核对象使用引用计数来表示有多少拥有者,内核对象中也记录了那个线程或进程是拥有者。当我们调用返回handle的函数时,引用计数会加1,调用CloseHandle函数,引用计数会减1.当引用计数为0时,内核对象将被操作系统从内存中销毁。

       常见的内核对象:进程,线程,文件,事件,信号量,互斥器,管道。

       释放内核对象:CloseHandle(HANDLE hHandle);//引用计数减1.

       如果进程结束前没有对它所调用的内核对象调用CloseHandle函数,系统会自动对这些内核对象引用计数减1.但,建议一定要调用CloseHandle函数,因为可能造成资源泄漏。

       线程内核对象:当线程正在执行时,处于未激发状态;当线程结束运行时,处于激发状态。当线程内核对象处于激发对象时,其他等待的线程,都会被唤醒。

       进程内核对象:当进程正在执行时,处于未激发状态;当进程结束运行时,出于激发状态。

       文件内核对象:当console窗口的输入缓冲区有数据可用时,将处于激发状态。

       事件内核对象:通过CreateEvent函数来设置开始时的状态。其可以分为人工重置和自动重置。人工重置,可以通过SetEvent函数和ResetEvent函数来设置状态。自动重置,当等待函数返回后,自动将事件内核对象设置为激发状态。

       信号量内核对象:当计数器的值大于0时,处于激发状态;当计数器等于0时,处于未激发状态。

       互斥内核对象:如果一个互斥量没被任何线程拥有,它将处于激发状态;一旦一个等待互斥量的函数返回时,它将自动被设置为未激发状态。

2.线程内核对象

       线程内核对象引用到的那个线程也会另这个内核对象开启,因此线程内核对象的默认计数为2,当调用CloseHandle时,引用计数减1,当线程结束时,引用计数再减1.

       win32函数错误处理:一个宏:MTVERITY。如果win32函数执行失败,其会打印一行文字说明.

3.线程函数

(1).创建线程的函数:CreateThread(...),其调用成功会返回一个线程的handle,调用失败会返回FALSE。其还会传回一个线程ID。

     线程函数的原型为:DROD  WINAPI 函数名(LVOID  参数名);

     handle是一个内核对象,实际上是一个指针,其指向内存的某样东西,你不能够直接取得这样东西。handle为进程所有。

     线程ID可以表示系统中任意进程中的线程,即时唯一的。

(2).将线程挂起调用函数:SuspendThread()(可以将其他线程挂起);

(3).将挂起的线程恢复执行调用函数:ResumeThread();

(4).结束线程:VOID ExitThread(DWORD dwExitCode);

(5).获取线程结束代码:GetExitCodeThread(HANDLE,LPDWORD);

     //执行成功返回TRUE,执行失败返回FLASE,可以通过函数GetLastError获取失败原因。如果线程仍在执行,则第二个参数为STILL_ACTIVE。

等待函数:

       (1):WaitForSingleObject();等待目标内核对象变为激发状态,返回WAIT_OBJECT_0;时间结束,返回WAIT_TIMEOUT;

       (2):WaitForMultipleObject():可以等待多个目标内核对象,有两种情况的设置:一种为等待对象中有一个对象处于激发状态就返回,一种为全部对象处于激发状态才返回。其返回值有:WAIT_TIMEOUT;WAIT_OBJECT_0;返回值-WAIT_OBJECT_0;WAIT_FAILED;当等待对象有互斥量时,返回值可能为:WAIT_ABANDONED_0~WAIT_ABANDONED_0_nCount-1;

       (3):MsgWaitForMultipleObject():它会在对象被激发或消息到达队列时被唤醒时返回。其相比WaitForMultipleObject函数多一个返回值WAIT_OBJECT_0+nCount,表示消息到达消息队列。

 

 线程同步

 1.临界区

       临界区不是内核对象,其只在存在同一个进程中,也就是说只能同一个进程的线程才能使用它。当一个线程通过EnterCriticalSection函数进入临界区后,可以多次重复进入该临界区,离开时也应该调用相同次数的Leave函数。

   使用方法:1)声明一个类型为CRITICAL_SECTION的变量;

                      2)使用InitializeCriticalSection()函数对该变量进行初始化。

                      3)线程使用EnterCriticalSection()函数进入临界区,这样该临界区被锁定,其它线程就不能访问了。

                      4)线程是用LeaveCritcalSection()函数离开临界区,其他线程获得禁区临界区的机会。

                      5)使用 DeleteCriticalSection()函数对该变量进行清除。

   注:如果进入临界区的线程结束了或死掉了,但没有调用LeaveCriticalSection函数,系统是没有办法将该临界区清除的,因为临界区不是内核对象。

2. 互斥器

      互斥器是内核对象,其能够被不同进程的线程拥有,其相对于临界区花费的时间更多(大约100倍)。

      当没有线程拥有互斥器时,处于激活状态(应该说:当线程没有被任何线程拥有时,该互斥器处于未激活状态。当有一个Wait函数处于等待状态时,互斥器会短暂处于激活状态,这样等待函数得以返回,而互斥器就变为未激活和状态)。当有线程拥有互斥器时,处于未激活状态。

    1)创建一个互斥器:CreateMutex().   如果创建的互斥器已经存在(名称存在),则会返回一个已经存在的互斥器的句柄。

        通过名字打开一个互斥器:OpenMutex().

    2)释放互斥器:ReleaseMutex();  当你用等待函数获取拥有权后,都要调用该函数,已释放拥有权。

    3)CloseHandle();

      如果拥有该互斥量的线程在结束时(不论是调用ExitThread还是死掉),没有释放掉这个互斥量(即调用ReleaseMutex),那么这个互斥量会被视为“未拥有”及“未激活状态”。而这时,如果有WaiForSingleObject函数处于等待状态会返回WAIT_ABANDONED_0,如果是WaitForMultipleObject函数则返回WAIT_ABANDONED_0到WAIT_ABANDON_0+n-1中的一个值,表示那个互斥器处于该状态。

3.信号量

    大于0.处于激发状态,等于0处于未激发状态。   

  和互斥量的区别:(1)对于信号量,一个线程可以反复调用Wait函数参数新的锁定,因为信号量不会有“拥有权”问题。而对于互斥器来说,一个拥有互斥器的线程不论调用多少次Wait函数,都不会处于阻塞态。(2)和互斥器不同,调用ReleaseSemaphore函数的线程不一定是调用等待函数的那个线程。任何线程在任何时间都可以调用ReleaseSemaphore函数,来释放任何线程锁定的信号量。

       1)创建信号量:CreateSemaphore()

            运用名称打开一个已经存在的信号量:OpenSemaphore()

      2)解除锁定:ReleaseSemaphore()    当你用等待函数后,都要调用。将其值加一,加一后的值不会大于最大值。

      3)CloseHandle()

4.事件

     事件和互斥器、信号量一样都是内核对象。事件的唯一目的是成为激发状态或未激发状态,状态由程序控制,不受等待函数的影响(副作用)。

      1)创建事件:CreateEvent()   可以通过该函数来设置事件的起始状态,以及状态是人工重置还是自动重置。

           打开已有事件对象:OpenEvent()

      2)CloseHandle();

       SetEvent():将事件设置为激发状态,

      人工重置:调用ResetEvent函数将事件设置为未激发状态。

      自动重置:当事件处于激发态(唤醒一个线程后)后,事件自动重置为未激发状态。

      PluseEvent:当是人工重置时,将事件设为激发状态,唤醒所有等待的线程,然后事件恢复未激发状态;当是自动重置时,将事件设为激发状态,唤醒一个等待的线程,然将事件设为未激发状态。

5.Interlocked函数

      Interlocked函数对32的变量进行操作,无“等待”机能,其有两个函数(都只能和0比较):

     1)InterlockedIncrement():递增

     2)InterlockedDecrement():递减

     3)InterlockedExchang():锁定一个新值,返回旧值

 

结束线程

    1.使用TerminateThread()函数强制结束函数。

       造成的影响:线程没来得及清理自己;线程的堆栈没释放;只是将线程变为激活状态。因此尽量不要用这个函数。

    2.调用函数:ExitThread();

    3.线程函数正常结束。

 

优先级

决定线程优先级的三个因素分别为:优先级类别、优先级层次和动态提升值。

 

1.优先权类别

4个优先权类别:      

      HIGH_PRIORITY_CLASS     基础优先权值为13;IDLE_PRIORITY_CLASS   基础优先权值为4;

       NORMAL_PRIORITY_CLASS  基础优先权值为7或者8;REALTIME_PRIOTITY_CLASS 基础优先权为24;

  优先权类别使用于进程不适用于线程。可以通过SetPriorityClass函数和GetPriorityClass函数来调整和获取优先级类别值。

 2.优先级层次

     优先级层次是对进程的优先权类别的修改,其用于调整进程中线程的相对重要性。可以通过SetThreadPriority函数和GetThreadPriority函数来改变和获取优先级层次的值。

7个优先级层次为:

     THREAD_PRIORITY+HIGHEST    调整值为:+2

     THREAD_PRIORITY_ABOVE_NORMAL   调整值为:+1

     THREAD_PRIROTY_NORMAL     调整值为:0

     THREAD_PRIROTY_BELOW_NORMAL   调整值为:-1

     THREAD_PRIROTY_LOWEST     调整值为:-2

     THREAD_PRIROTY_IDLE            调整值为:Set to 1

     THREAD_PRIROTY_TIME_CRITICAL        调整值为:Set to 15

  3.动态提升

动态提升是指:对优先权的一种调整,是系统能够机动对待线程,已强化程序的可用性。

在下列5种情况下,Windows 会提升线程的当前优先级:

  A)   I/O操作完成

  B)   信号量或事件等待结束

  C)   前台进程中的线程完成一个等待操作

  D)   由于窗口活动而唤醒图形用户接口线程

  E)    线程处于就绪状态超过一定时间,但没能进入运行状态(处理机饥饿)

重叠I/O(overlapped I/O)
win32的文件操作函数

       A)  CreateFile():用于打开资源各种资源,包括(不限于)文件、串行口和并行口、命名管道、Console。

       B)  ReadFile()和WriteFile():可以同时读文件的不同部分,没有目前文件位置的概念。

OVERLAPPED结构体:

typedef struct _OVERLAPPED{

        DWORD  Internal;

        DWORD  InternalHigh;

        DWORD  Offset;//文件开始读火邪相对文件头开始的便宜位置

        DWORD  OffsetHigh;

        HANDLE hEvent;一个手动充值的事件内核对象,在重叠I/0完成即被激发、

  }OVERLAPPED,*LPOVERLAPPED;

 被激发的事件内核对象

       使用重叠I/O并搭配事件内核对象,使用WaitForMultipleObject函数,最多才能等待MAXIMUM_WAIT_OBJECTS个对象(一般操作系统定义为64个),再多就无法处理了。在一个就是你需要不断的判断是那个handle被激发。

 异步过程调用

         为了解决上面使用内核对象带来的两个问题,我们可以使用带“EX”的两个ReadFile和WriteFile函数。这两函数多了一个参数,用于指定回调函数。这个函数只有在重叠I/O完成后,且系统处于“alertable”状态时,才会调用。当系统不处于“alertable”状态时,回到函数会等待其处于该状态后在运行。以下5个函数处于等待状态,会将“alertable”标记设为TRUE,因而处于“alertable”状态:

        A) SleepEx()

        B) WaitForsingleobjectEx()

        C) WaitForMultipleObjectEx()

        D) MsgWaitForMultipleObjectEx()

        E) SignalObjectAndWait()

回调函数的原型: 

      VOID WINAPI FileIOCompletionRoution(

            DWORD   dwErrorCode,

            DWORD   dwNumberOfBytesTransferred,

            LPOVERLAPPED  lpOverlapped

       );               
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: