实现线程同步的几种方法

1.程序、进程和线程

    这是几个比较容易混淆的概念。

    程序是计算机指令的集合,它以文件的形式存储在磁盘上。程序不能申请系统资源,不能被系统调度,也不能作为独立的运行单位。

    进程是一个正在运行的程序实例,是一个程序在其自身的地址空间中的一次执行活动。我们编写的程序在编译后生成的后缀为.exe的可执行程序,是以文件的形式存储在磁盘上的,当运行这个可执行程序时,就启动了该程序的一个实例,即一个进程。进程是资源申请、调度和独立运行的单位,它使用系统中的运行资源。 

   线程的划分尺度小于进程,线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。线程自己基本上不拥有系统资源,或只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行。

    一个程序可以对应多个进程,一个进程至少包含一个线程。

2.多线程容易出现的问题

    在包含多个线程的进程中,多个线程共享系统资源, 这种资源共享模式大大地节约了系统开销,但也造成了一个问提:就是各进程在资源访问过程中所有权的冲突问题。例如,时刻T1,线程1对资源A进行访问,在对其操作之前,CPU分配的时间片已到,系统将进程切换到线程2,假设线程2对资源A有一个写操作,那么当线程2结束,系统再次切换到线程1时,资源A已经发生了变化,此时线程1对资源A的操作就不是我们预想的操作了,因为这期间资源本身发生了变化。

    为了解决资源共享模式下多个线程访问同一个资源的问题,就必须在多个线程之间实现一个同步处理,以保证一个线程访问这个资源时其他线程不能访问该资源。

3.线程同步的几种实现方法

(1).利用互斥对象实现线程同步

    互斥对象(mutex)属于内核对象,它能够确保线程拥有对单个资源的互斥访问权。互斥对象包含一个使用数量,一个线程ID和一个计数器。

    利用互斥对象实现线程同步,首先必须创建一个互斥对象,调用CreateMutex函数,并设定该互斥对象的初始状态;在分线程中,线程必须主动申请共享对象的使用权才能获得该所有权,调用WaitForSingleObject函数来实现;当线程对共享资源访问结束后,应释放该对象的所有权,也就是让该对象处于已通知状态。

    相关函数原形如下:

HANDLE CreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes,  BOOL bInitialOwner,  LPCTSTR lpName);

BOOL ReleaseMutex(HANDLE hMutex);

DWORD WaitForSingleObject(HANDLE hHandle,DWORD dwMilliseconds);

(2).利用事件对象实现线程同步

    事件对象也属内核对象,它包含三个成员:使用计数;指明该事件是自动重置事件还是人工重置事件的标志位;指明该事件处于已通知状态还是未通知状态的标志位。

    利用事件对象实现线程同步的过程与互斥对象大体相似:首先创建一个事件对象,调用CreateEvent函数;设置事件对象的状态,使用SetEvent函数;在分线程中调用WaitForSingleObject函数申请该对象的使用权;当线程结束后,调用ResetEvent函数重置事件对象状态。

    相关函数原形如下:

HANDLE CreateEvent(LPSECURITY_ATTRIBUTES lpEventAttributes,BOOL bManualReset, BOOL bInitialState, LPCTSTR lpName ); 

BOOL SetEvent(HANDLE hEvent);
BOOL ResetEvent(HANDLE hEvent);
(3).利用关键代码段实现线程同步

     关键代码段,也称为临界区,工作在用户方式下。它是指一小段代码段,在代码能够执行前,它必须独占对某些资源的访问权。

    关键代码段的原理与公用电话亭比较类似。当我们要使用公用电话时,首先要判断电话亭里是否有人,如果有人正在使用,那么我们只有在外面等待;当那个人使用完电话并离开时,我们才能进入电话亭使用电话。利用关键代码段实现线程同步,首先要创建“公用电话亭”这一公用资源,即创建CRITICAL_SECTION类型的结构体;其次要对该关键代码段初始化,可以调用InitializeCriticalSection来实现;现在有了一个“公用电话亭”,要想进去“使用电话”,必须要判断里面是否有人,需要调用EnterCriticalSection函数;在使用完了电话后,我们要离开电话亭并交出电话的使用权,此时需要调用LeaveCriticalSection,通知其他人电话现在闲置了,可以用了。

    相关函数原形如下:

void InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
void EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
void LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
4.以上几种方法的比较

(1).互斥对象和事件对象都属于内核对象,使用内核对象实现线程同步时,速度较慢,但可以在多个进程中的各个线程间进行同步

(2).关键代码段工作在用户方式下,同步速度较快,但容易进入死锁状态

5.关于内核对象

    内核对象是系统用来存放关于进程的统计信息的地方,是操作系统内部分配的一个内存块,该内存块是一种数据结构,其成员负责维护该对象的各种信息。由于内核对象只能被内核访问使用,因此应用程序在内存中无法找到该数据结构,并直接改变其内容,只能通过Windows提供的一些函数来对内核对象进行操作。

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值