论文部分内容阅读
摘 要:针对实时数据监控系统普遍存在的数据同步问题提出改进方法,利用Windows内核对象的特性编程设计了共享锁类,并与未使用锁、使用互斥锁两种情况进行时序比较,结果表明共享锁在同步数据和优先保证写者效率方面具有明显的优越性。这些特性使共享锁在工业控制级别的实时软件系统中具有重要的现实意义。
关键词:数据同步共享锁实时数据监控
中图分类号:TM73 文献标识码:A 文章编号:1674-098X(2011)05(c)-0018-01
1 引言
在工控组态软件领域,普遍存在一个称作读者写者的问题,即对某些资源的访问,存在两种可能的情况,一种访问必须是排他的,称作写操作;另一种访问可以是共享的,称为读操作。对于一个实时数据监控系统来说,由于Windows是一个多任务抢占式的操作系统,在多个线程共同访问一个数据区的环境下,会产生数据同步的问题。比如:有提供数据的服务程序A和若干界面显示程序B、C、D等。服务程序和显示程序共同使用一块数据内存区E,其中服务程序负责更新内存数据(写操作),而显示程序则定时访问内存区数据(读操作)。假设数据内存区E中变量的数据结构为:
FILETIMEftTime;//时间
DWORD dwValue;//值
那么,在B刚读取完某个变量的ftTime值后,插入了A的写操作,这时候再读取到的dwValue值就和ftTime不一致了。这种不一致的现象必须通过数据同步来避免。所以,对内存区E的读写访问必须作以下两个约束:
1)A正在进行写操作时,B、C、D必须等待该操作完成才能访问E;约束条件(1)
2)B、C、D正在访问E时,A 必须等待该操作完成才对E进行写操作;约束条件(2)
以上约束可以简单的归纳为:读写互斥。
2 一般解决方法
在Windows下,系统提供了事件、互斥量、信号量等核心对象以及一些等待函数来进行多线程同步。单独使用这些核心对象均能够实现读写的互斥。
但是单纯的实现读写互斥并不能完全满足实时监控系统对访问效率的要求。因为读操作的并发并不会引起数据的不同步,为了提高效率,必须在之前的两个约束的基础上增加读写访问的另一约束,即约束(3):B、C、D能够同时访问E。
3 共享锁
3.1 数据结构
利用Windows内核对象的特性,可以编程实现满足上述三点约束条件的共享锁类。
共享锁的内部数据结构由读事件、写事件、互斥对象和读者计数组成。
其中,读、写事件是一对命名的、手动重置的事件,初始化为有信号状态。事件可以使用Windows Api函数CreateEvent、ResetEvent和SetEvent 来创建、重置和置位。这对事件成员的作用是实现读写的互斥。
共享锁中的互斥对象是一个初始化无所有者的命名互斥量。互斥量可以使用CreateMutex、WaitForSingleObject和ReleaseMutex来创建、进入和离开。互斥对象在共享锁中的作用有两个:对写操作进行保护,保证一个时刻内只有一个线程在进行写操作;在多个读线程中保护读者计数,保证所有读线程中对读者计数的操作都是原子操作。
读者计数实际上是一片命名的内存区,可以用CreateFileMapping和MapViewOfFile来创建和映射该内存区。读者计数用于对同一时刻内的并发读操作进行管理。
3.2 读写操作流程
3.2.1 写操作
数据服务A在对数据内存区E進行写数据操作时,首先重置写事件。这时候写事件为无信号状态,这就意味着B、C、D等读线程将在“等待 写”这一步中挂起,不再进行读操作。接下来A将等待读事件,如果之前时刻已经有若干线程在进行读操作,那么A将等到这些操作完成后进入互斥对象。这就意味着A已经独占了内存区E这块资源,可以放心地进行写操作了。
在A完成写操作后,首先离开互斥对象,释放之前独享的这块资源,再置位写事件,让B、C、D等读线程结束挂起状态,进行读操作。
3.2.2 读操作
假设B线程某一时刻想以读者的身份访问数据区,B首先等待写事件,如果写操作正在进行,那么B将被挂起直到写操作完成。接下来B将访问读者计数,如果读者计数为0,B将重置读事件;否则意味着在前一时刻已经有另外的读者(C或者D)已经重置了读事件,B将读者计数自增后直接进入读操作。当然,在访问读者计数的整个过程中都使用互斥对象对计数进行保护。
在完成读操作后,B再次访问读者计数字,如果发现此时B已经是最后一位读者(计数=1),B将置位读事件,计数自减后退出。
4 比较和结果
4.1 实验平台的搭建
创建一个写者线程,三个读者线程,同时运行。在每个线程内,记录下每次操作的性质(读或写)、操作时间和操作者(线程),保存到日记文件中,是为原始数据。分三次进行实验:1无数据同步;2使用互斥对象进行数据同步;3使用文中所述共享锁进行数据同步。
4.2 比较和结果
未使用锁情况下,由于Windows是抢占式的操作系统,A、B、C、D四个进程将对E资源进行无序的竞争。
使用了互斥锁情况下,在B、C、D的读周期内不可能再出现值被A修改的情况。但是这样的数据同步的缺陷也是明显的:在B的读周期内A、C和D都在挂起等待状态,消耗了不必要的时间;另外,在这样的数据同步模式下,读者和写者的地位是平等的,A作为数据的提供者却必须要和B、C、D同等的去抢占资源,而且读者越多负担越重,是一种很不合理的作法。同样,在B、C、D的读周期内数据不会被A改动,解决了数据同步的问题。另外,B、C、D可以并发进行,因为多个读者访问不会对资源E造成破坏,避免了A不必要的等待时间,提高了效率。
更重要的是,共享锁对读者和写者的权限进行了区分:写者优先于读者访问共享资源。这是因为任意时刻下A想对资源E进行写操作时都会重置写事件,这时试图进行读操作的其他读者就被迫进入等待状态,A只需要等待前一时刻已经进入读周期的进程完成操作。这就保证了服务程序A的工作效率,也就是保证数据的实时性。
5 结语
综上所述,共享锁具有同步数据和优先保证写者效率的工作特性。这些独特的工作使得这样的共享锁在工业控制级别的实时软件系统中具有很重要的现实意义。在实时数据库、实时监控系统、数据报警系统等领域的软件开发中具有广泛的应用前景。
参考文献
[1] Jeffrey Richter. Windows核心编程[M].北京:机械工业出版社,2008,5.
关键词:数据同步共享锁实时数据监控
中图分类号:TM73 文献标识码:A 文章编号:1674-098X(2011)05(c)-0018-01
1 引言
在工控组态软件领域,普遍存在一个称作读者写者的问题,即对某些资源的访问,存在两种可能的情况,一种访问必须是排他的,称作写操作;另一种访问可以是共享的,称为读操作。对于一个实时数据监控系统来说,由于Windows是一个多任务抢占式的操作系统,在多个线程共同访问一个数据区的环境下,会产生数据同步的问题。比如:有提供数据的服务程序A和若干界面显示程序B、C、D等。服务程序和显示程序共同使用一块数据内存区E,其中服务程序负责更新内存数据(写操作),而显示程序则定时访问内存区数据(读操作)。假设数据内存区E中变量的数据结构为:
FILETIMEftTime;//时间
DWORD dwValue;//值
那么,在B刚读取完某个变量的ftTime值后,插入了A的写操作,这时候再读取到的dwValue值就和ftTime不一致了。这种不一致的现象必须通过数据同步来避免。所以,对内存区E的读写访问必须作以下两个约束:
1)A正在进行写操作时,B、C、D必须等待该操作完成才能访问E;约束条件(1)
2)B、C、D正在访问E时,A 必须等待该操作完成才对E进行写操作;约束条件(2)
以上约束可以简单的归纳为:读写互斥。
2 一般解决方法
在Windows下,系统提供了事件、互斥量、信号量等核心对象以及一些等待函数来进行多线程同步。单独使用这些核心对象均能够实现读写的互斥。
但是单纯的实现读写互斥并不能完全满足实时监控系统对访问效率的要求。因为读操作的并发并不会引起数据的不同步,为了提高效率,必须在之前的两个约束的基础上增加读写访问的另一约束,即约束(3):B、C、D能够同时访问E。
3 共享锁
3.1 数据结构
利用Windows内核对象的特性,可以编程实现满足上述三点约束条件的共享锁类。
共享锁的内部数据结构由读事件、写事件、互斥对象和读者计数组成。
其中,读、写事件是一对命名的、手动重置的事件,初始化为有信号状态。事件可以使用Windows Api函数CreateEvent、ResetEvent和SetEvent 来创建、重置和置位。这对事件成员的作用是实现读写的互斥。
共享锁中的互斥对象是一个初始化无所有者的命名互斥量。互斥量可以使用CreateMutex、WaitForSingleObject和ReleaseMutex来创建、进入和离开。互斥对象在共享锁中的作用有两个:对写操作进行保护,保证一个时刻内只有一个线程在进行写操作;在多个读线程中保护读者计数,保证所有读线程中对读者计数的操作都是原子操作。
读者计数实际上是一片命名的内存区,可以用CreateFileMapping和MapViewOfFile来创建和映射该内存区。读者计数用于对同一时刻内的并发读操作进行管理。
3.2 读写操作流程
3.2.1 写操作
数据服务A在对数据内存区E進行写数据操作时,首先重置写事件。这时候写事件为无信号状态,这就意味着B、C、D等读线程将在“等待 写”这一步中挂起,不再进行读操作。接下来A将等待读事件,如果之前时刻已经有若干线程在进行读操作,那么A将等到这些操作完成后进入互斥对象。这就意味着A已经独占了内存区E这块资源,可以放心地进行写操作了。
在A完成写操作后,首先离开互斥对象,释放之前独享的这块资源,再置位写事件,让B、C、D等读线程结束挂起状态,进行读操作。
3.2.2 读操作
假设B线程某一时刻想以读者的身份访问数据区,B首先等待写事件,如果写操作正在进行,那么B将被挂起直到写操作完成。接下来B将访问读者计数,如果读者计数为0,B将重置读事件;否则意味着在前一时刻已经有另外的读者(C或者D)已经重置了读事件,B将读者计数自增后直接进入读操作。当然,在访问读者计数的整个过程中都使用互斥对象对计数进行保护。
在完成读操作后,B再次访问读者计数字,如果发现此时B已经是最后一位读者(计数=1),B将置位读事件,计数自减后退出。
4 比较和结果
4.1 实验平台的搭建
创建一个写者线程,三个读者线程,同时运行。在每个线程内,记录下每次操作的性质(读或写)、操作时间和操作者(线程),保存到日记文件中,是为原始数据。分三次进行实验:1无数据同步;2使用互斥对象进行数据同步;3使用文中所述共享锁进行数据同步。
4.2 比较和结果
未使用锁情况下,由于Windows是抢占式的操作系统,A、B、C、D四个进程将对E资源进行无序的竞争。
使用了互斥锁情况下,在B、C、D的读周期内不可能再出现值被A修改的情况。但是这样的数据同步的缺陷也是明显的:在B的读周期内A、C和D都在挂起等待状态,消耗了不必要的时间;另外,在这样的数据同步模式下,读者和写者的地位是平等的,A作为数据的提供者却必须要和B、C、D同等的去抢占资源,而且读者越多负担越重,是一种很不合理的作法。同样,在B、C、D的读周期内数据不会被A改动,解决了数据同步的问题。另外,B、C、D可以并发进行,因为多个读者访问不会对资源E造成破坏,避免了A不必要的等待时间,提高了效率。
更重要的是,共享锁对读者和写者的权限进行了区分:写者优先于读者访问共享资源。这是因为任意时刻下A想对资源E进行写操作时都会重置写事件,这时试图进行读操作的其他读者就被迫进入等待状态,A只需要等待前一时刻已经进入读周期的进程完成操作。这就保证了服务程序A的工作效率,也就是保证数据的实时性。
5 结语
综上所述,共享锁具有同步数据和优先保证写者效率的工作特性。这些独特的工作使得这样的共享锁在工业控制级别的实时软件系统中具有很重要的现实意义。在实时数据库、实时监控系统、数据报警系统等领域的软件开发中具有广泛的应用前景。
参考文献
[1] Jeffrey Richter. Windows核心编程[M].北京:机械工业出版社,2008,5.