博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Windows几种线程同步方法介绍
阅读量:6947 次
发布时间:2019-06-27

本文共 3428 字,大约阅读时间需要 11 分钟。

系统中的所有线程都要访问系统资源,一个线程霸占某个资源,其他需要该资源的线程就不能完成自己的任务;另外如一个线程在读取某块内存中的数据,而另一个线程又正在修改这块内存的值,这同样不是我们想要的,所以线程之间必须要有一套自己的规则,不然就凌乱了。线程之间需要通信,如A线程霸占某个B线程需要的资源X,在A占用期间,B线程只能等待,或处于挂起状态,当A线程用完资源X后,系统会告诉线程B,资源X可以用了,或是将处于挂起状态的线程B唤醒,然后线程B就获得对资源X的控制权,其他想用资源X的线程就得经历B刚才的遭遇。当多个线程同时需要某个资源时必须遵守下面两个规则:

1:多个线程“同时”访问资源,不能破坏资源的完整性。

2:一个线程需要通知其他线程某项任务已经完成。

原子访问:Interlocked系列函数。多线程编程大部分情况与原子访问有关,即一个线程在访问某个资源时,确保没有其他线程能访问该资源。

增量函数InterlockedExchangeAdd结构如下:

InterlockedExchangeAdd(

    unsigned long volatile *Addend,//被增量变量的地址

    unsigned long Value//增量值

)

Volatile表示每次都成内存中读取数据,而不会从高速缓存中读取数据,如一个全局变量,在一个多线程函数中被修改,在多核CPU中,这个变量可能在多个CPU的高速缓存中都有副本,如果不用volatile修饰,那么可能会因为优化的原因,CPU不会读内存中的数据,而是直接从高速缓存中读取数据,在这种情况下,很可能这个值已经被修改了,这样CPU读取到的不是最新的数据,程序肯定会出错,用volatile修饰后,这个变量的所有高速缓存就会失效,就不会出现这种问题。在多线程编程中volatile作用非常大,效率也最高。但他就是只能修饰单个变量,不能修饰代码段。

InterlockedExchangeAdd执行的速度是非常快的,只需要占用几个CPU周期。用InterlockedExchangeAdd来修改某个变量的值,好像有点大材小用了,因为用Volatile就足够了,简单迅速。但在实现旋转锁时InterlockedExchange就非常有用。旋转锁的代码大致如下:

bool sourceIsUse=false;        void fun()        {            //一直等待直到资源可用            while(InterlockedExchange(&sourceIsUse,true)==true)            {                Sleep(0);            }            //访问资源的操作            ......            //资源用好了,打开锁,让其他等待的资源访问            InterlockedExchange(&sourceIsUse,false);        }

 

InterlockedExchange:将第一个参数的值修改成第二个参数的值,返回第一个参数原来的值。在第一个线程就来的时候,它顺利的闯过了While循环,并上了锁,导致while始终为true,后来的线程就一直在while里面打转,当前面的线程用完之后,他就会把锁打开,然后新来的线程就可以跳出while循环,并上锁(在等待时一直在上锁),开锁独占资源了,新来的线程又开始等待。就像大厦前门的旋转门,一拨人进去之后,后面的人就只能在外面等,等里面的人出去之后,后面的人也就可以进去了,周而复始。

 

高速缓存行。当CPU从内存中读取一个字节时,它并不是真的只读一个字节,而是读取一个高速缓存行,一个高速缓存行可能是32个字节、64个字节或是128个字节,它始终读取的字节数是32的整数倍,这样CPU就不用非常频繁的读取内存,从而提高程序的性能,当CPU访问某块内存是它会访问这块内存旁边的内存的概率是非常大的,于是就一起读了。更多关于数据对齐的信息请看我的文章《》。

 

高级线程同步。刚刚简单的说了一下旋转锁,现在又来说旋转锁的坏,旋转锁的问题在于等待的线程一直在执行毫无用处的该死的死循环,浪费CPU的时间,这肯定是不能容忍的,虽然曾经一度容忍过它。当一个线程需要某个资源,而这个资源被另一个线程占用时,如果这个线程等了一会儿还不能获得这个资源,那么这个线程就应该被切换到等待状态,让系统充当该线程的代理,当该资源可以被使用时,系统就会将该线程唤醒,然后该线程就可以独占该资源。而实现这一功能的就是关键段。

 

关键段。关键段是一小段代码,在执行之前需要独占对一些共享资源的访问,这种方式可以让多行代码以原子的方式进行访问,当有一个线程对访问这段代码时其他线程只能等待。使用关键段的步骤如下:

CRITICAL_SECTION g_cs;//构造一个CRITICAL_SECTION实例

InitializeCriticalSection(&g_cs);//初始化g_cs的成员

EnterCriticalSection(&g_cs);//进入关键段

LeaveCriticalSection(&g_cs);//离开关键段

DeleteCriticalSection(&g_cs);//清理g_cs

EnterCriticalSection会检查结构CRITICAL_SECTION的成员变量,这些成员表示是否有线程正在访问资源,以及哪个线程正在访问资源,EnterCriticalSection会进行一些测试。如果没有线程正在访问资源,EnterCriticalSection会更新变量成员,以表示已经有线程正在访问资源,并马上从EnterCriticalSection返回,继续执行关键段中的代码,如果变量成员表示已经有线程正在访问资源,那么EnterCriticalSection会使用一个事件内核对象把线程切换成等待状态,等待状态的线程是不会浪费CPU的时间的,系统会记住这个线程想要使用这个资源,一旦当前线程调用LeaveCriticalSection,系统会自动更新CRITICAL_SECTION的成员变量,并将等待的线程切换成可调度状态。

LeaveCriticalSection会检查结构CRITICAL_SECTION的成员变量并将计数器减一,如果计数器变为0,LeaveCriticalSection会更新成员变量表示现在没有线程访问资源,若有等待的线程,则将等待的线程切换成可调度的状态。

 

当一个线程进入关键段时,若有线程正在访问关键段,那么系统就会将新的线程切换成等待状态,这意味着将线程从用户模式切换成内核模式,这个切换的开销大约是1000个CPU周期,这个开销其实是很大的,所以在EnterCriticalSection内部使用旋转锁,并不是马上将线程切换成等待状态,而是先用旋转锁试探一些,看线程是否释放了对资源的访问,如果释放了,新的线程就不用被切换成等待状态了,就可以直接访问资源了,也就是说花了旋转锁轮询的时间,如果旋转锁轮询了一段时间,线程还是没有释放资源,对不起系统就不会让它继续轮询了,因为系统也不知道还要轮询多久,毕竟轮询一直都是在消耗CPU的时间,系统会停止轮询,将新的线程切换成等待状态,当前一个资源释放对资源的访问,系统会将新的线程切换成可调度状态。

 

Silm读/写锁。SRWLock的目的和关键段是一样的,就是对资源的保护,不让其他线程访问。不同的是,它区分线程是读线程还是写线程。我们都是知道,一个资源可以同时被多个线程同时读,就是不能同时读,或是读写。也是是说写必须是独占的方式,而读可以以共享的方式访问。

读写锁调用的函数如下,跟关键段差不多,我就不废话了。

RTL_SRWLOCK lock;

InitializeSRWLock(&lock);

AcquireSRWLockExclusive(&lock);//独占的方式访问

ReleaseSRWLockExclusive(&lock);

AcquireSRWLockShared(&lock);//共享的方式访问

ReleaseSRWLockShared(&lock);

作者:

博客:

 

转载地址:http://qyenl.baihongyu.com/

你可能感兴趣的文章
思科HyperFlex系统提供最完整的新一代超融合解决方案
查看>>
中国电信明确物联网技术路标:7月启动Cat1
查看>>
市场需求持续增长,云计算或将成为资本市场下一个焦点
查看>>
《 FreeSWITCH权威指南》——3.6 小结
查看>>
JMeter的基本介绍和入门(1)
查看>>
网络电话让毕业季不再有“情感真空”
查看>>
疑似微信企业版曝光 网友留言称“心疼阿里”
查看>>
高通每天提供超过一百万颗芯片 助力物联网发展
查看>>
Python Selenium的js扩展实现
查看>>
全球最大规模窄带物联网智慧水务商用项目在福州启动
查看>>
iDTRONIC推出RFID平板和手持移动设备
查看>>
圆通速递率先推出隐形面单 为个人信息安全再添安全锁
查看>>
当200亿个物联网设备同时产生数据 是时候重新认识传感器了
查看>>
深度专访:深谈的故事 (LinuxDeepin)
查看>>
《构建高可用Linux服务器 第3版》—— 1.2 全面了解Linux服务器
查看>>
《开放复杂智能系统——基础、概念、分析、设计与实施》—第1章1.5节 小结...
查看>>
《测试驱动数据库开发》——2.4 增量构建
查看>>
网站图片优化你需要知道的地方
查看>>
《计算机科学概论》—第3章3.4节音频数据表示法
查看>>
深入理解Java内存模型(一)——基础
查看>>