线程和同步

来源:互联网 发布:淘宝直通车计算机出价 编辑:程序博客网 时间:2024/06/15 15:30

线程和同步

1. 多线程作用

  1. 提高应用程序响应。利用多线程编程并不一定能加快程序运行的速度,其目的是拥有更加良好的程序响应。例如在一个图形界面执行一个操作的同时启动另一个。
  2. 使多CPU系统更加有效。
  3. 改善程序结构。多线程程序比单线程程序更能适应用户需求的变更。
  4. 占用更小的系统资源。线程的数据结构简单,停止、运行速度快,进程是线程的靠山,是车间,线程是小组,活动方便。进程任务大、信息多,将它细分为线程后,各线程可共享进程分到的资源,调度方便。

2. 线程和同步

使用多进程于多线程时,有时需要协同两种或多种动作,此过程就称为同步(Synchronization)

  • 等待函数

    Win32 API 提供了一组能使线程阻塞其自身的等待函数。这些函数在其参数中的一个或多个同步对象产生了信号,或者超过规定的等待时间才会返回。在等待函数未返回时,线程处于等待状态,此时线程只消耗很少的CPU时间。使用等待函数即可以保证线程的同步,又可以提高程序的运行效率。

DWORD WINAPI WaitForSingleObject(  _In_ HANDLE hHandle,  _In_ DWORD  dwMilliseconds);//检测一个同步对象
DWORD WINAPI WaitForMultipleObjects(  _In_       DWORD  nCount,  _In_ const HANDLE *lpHandles,  _In_       BOOL   bWaitAll,  _In_       DWORD  dwMilliseconds);//检测多个同步对象
  • 互斥体对象

    Mutux对象的状态在它不被任何线程拥有时才有信号,而当它被线程拥有时则无信号。Mutex对象很适合用来协调多个线程对共享资源的互斥访问。

    可以按下列步骤使用该对象:

    1. 建立互斥体对象,得到句柄

      HANDLE CreateMutex();
    2. 在线程可能产生冲突的区域前(即访问共享资源之前)调用WaitForSingleObject,将句柄传给函数,请求占用互斥对象

      dwWaitResult = WaitForSingleObject(hMutex, 5000);
    3. 共享资源访问结束,释放对互斥体对象的占用

      ReleaseMutex(hMutex);

      互斥体对象在同一时刻只能被一个线程占用,当互斥体对象被一个线程占用时,若有另一个线程想占用它,则必须等到前一线程释放后才能成功。

  • 信号对象

    信号对象允许同时对多个线程共享资源进行访问,在创建对象时指定最大可同时访问的线程数。当一个线程申请访问成功后,信号对象中的计数器减1,调用ReleaseSemaphore函数后,信号对象中的计数器加1。其中,计数器值大于或等于0,单小于或等于创建时指定的最大值。如果一个应用在创建一个信号对象时将其计数器的初始值设为0,则阻塞了其他线程,保护了资源。等初始化完成后,调用ReleaseSemaphore函数将其计数器增加至最大值,则可进行正常的存取访问。

    可以按下列步骤使用该对象:

    1. 创建信号对象或打开一个信号对象

      HANDLE CreateSemaphore();//或者打开一个对象HANDLE OpenSemaphore();
    2. 在线程访问共享资源之前调用WaitForSingleObject。

    3. 在共享资源访问完成后,应释放对信号对象的占用

      ReleaseSemaphore();
  • 事件对象

    事件对象(Event)是最简单的同步对象,它包括有信号和无信号两种状态。在线程访问某一资源之前,需要等待某一事件的发生,这是用事件对象最合适。例如:只有在通信端口缓冲区收到数据后,监视线程才被激活。

    事件对象是用CreateEvent函数建立的。该函数可以指定事件对象的类和事件的初始状态。如果是手工重置事件,那么它总是保持有信号状态,直到用ResetEvent函数重置成无信号事件。如果是自动重置事件,那么它的状态在单个等待线程释放后会自动变为无信号的。用SetEvent可以把事件对象设置成有信号状态。在建立事件时,可以为对象命名,这样其他进程中的线程可以用OpenEvent函数打开指定名字的事件对象句柄。

  • 排斥区对象

    在排斥区中异步执行时,它只能在同一进程的线程之间共享资源处理 。虽然此时上面介绍的几种方法均可使用,但是,使用排斥区的方法则使同步管理的效率更高。

    使用时先定义一个CRITICAL_SECTION结构的排斥区对象,在进程使用之前调用如下函数对对象进行初始化:

    VOID InitializeCriticalSection(LPCRITICAL_SECTION)

    当一个线程使用排斥区时,调用函数EnterCriticalSection或TryEnterCriticalSection。

    当要求占用、退出排斥区时,调用函数LeaveCriticalSection释放对排斥区对象的占用,供其他线程使用。