最近比较忙,很久没有写博客了,持续长时间的编程,使得我完全沦为程序匠人。但是感觉却不是想别人那么糟糕,毕业已经快两年了,我为我的编程兴趣仍然如此强烈而感到欣慰,也对一直以来比较关心的“行业应用软件架构设计”有了更深的了解,这坚定了我的信念!
今天晚上,终于有了一点点闲暇的时间,就想大家分享下“多线程程序操作共享区域(文件)”的一些体会吧!
多线程相信大家都陌生吧,多线程程序操作共享区域应该也不陌生吧,但是大家是否经历过多CPU的服务器下同时100个线程,操作离散的文件呢?如果每个线程只负责一个固定的文件,那么问题也就不是问题了,但是如果离散的文件,有可能同时被多线程都读写的,那么是否能保证文件读写的数据一致性、是否能保证数据在存储的时候由于非次序存储而导致数据丢失呢?另外,又怎么解决同一段时间同一个线程操作同一个文件的效率问题呢,如果有方法解决,有怎么保证不出现上述第一个问题呢?
摆在面前的二个问题,其实都是编写程序时候,尤其是性能要求比较高的时候,特别需要注意的问题。如下将一一介绍我的一些体会;
如何解决多线程多文件操作的数据一致和丢失问题
解决这个问题,我们要先思考一下,究竟是什么原因导致这个问题呢?答案很明显,主要因为多线程多文件存储时候的时候频繁打开和频繁关闭无次序性导致的,这有点想数据库一样,DBMS是怎么来解决这个问题呢?DBMS有两个方法,一是事务、二是双端锁,其实这两个方法是一个样的道理,在这里我们就不做介绍,有兴趣大家可以查寻些其他的资料。这里我介绍实际应用中我的方法。
我的方法是,保证一个文件在操作的时候,只打开一个实例,打开之后只有一次关闭。
首先来看看,我是如何来设计这样的文件处理单元,我们起名为CDataHandlerUint,大家当作其为结构体就可以了,如下代码;
typedef struct _DATA_HANDLER_UINT{queue<bool> m_OpenStatusQ; TiXmlDocument m_doc;CString m_strXmlPath;DWORD m_dwOldTicks;}CDataHandlerUint,*PCDataHandlerUint;
由于我们操作XML文件,因此在这个结构体中,我们保存了一个打开的XML文件实例——m_doc。另外一个重要的成员是m_OpenStatusQ这个暂时命名为XML文件打开请求队列,用于记录文件读写次数。其他成员随后会逐一介绍。
如下来看看,这个结构体在线程内部有是怎么用的,我们还需要一个关键的管理这个结构体的成员,CMap<CString,LPCSTR,PCDataHandlerUint,PCDataHandlerUint> m_mapHandlerUint,m_mapHandlerUint就用来管理这个结构体的。其中m_strXmlPath就是这个MAP的KEY,用来直接搜索出CDataHandlerUint的数据处理单元的pointer。
那么在线程中又怎么来应用这样的一个成员呢?看下如下这个线程的处理过程就明白了;
UINT32 DataHandlerThread(LPVOID pThis){
(1) 获取要操作的文件路径
(2) 通过文件路径,获取保存在m_mapHandlerUint的Pointer.
(3) 如果这个Pointer为NULL,重新NEW,调用m_OpenStatusQ.push(true),之后并添加到MAP中。
(4)如果这个Pointer不为NULL,调用m_OpenStatusQ.push(true)。
............
(5) 调用m_OpenStatusQ.pop(),之后检测m_OpenStatusQ的size ,如果size为0,保存并关闭XML文件,否则,在根据m_dwOldTicks判断是否超时,如果超时同样保存并关闭XML文件,否则结束函数
}
这样就应用了MAP加上queue顺利的解决了这个问题,大家看了之后,仔细想想吧!
如果线程操作文件时,如果文件存在一定顺序,那么怎么提高效率呢?
如果一个线程序遇到一个这样的文件系列怎么办,如下文件系列;
A.xml
A.xml
B.xml
B.xml
C.xml
......
从这里可以看出,文件是有顺序的,那么如何提高效率呢?
答案就是,尽量减少文件打开和关闭次数,重复利用已经打开的文件句柄!
这里程序设计的方法其实很简单,只需获取上一次操作文件句柄,并在上次操作的时候不关闭文件,关闭文件的操作放在调用之外,这样当发现两个句柄一样的时候,即调用保存并关闭,当然这里考虑到第一个问题,因此保存并关闭的条件仍然是调用m_OpenStatusQ.pop(),之后检测m_OpenStatusQ的size ,如果size为0,保存并关闭XML文件,否则,在根据m_dwOldTicks判断是否超时,如果超时同样保存并关闭XML文件,否则结束函数。
上述的两个问题,两个方法,只介于本人的体会,肯定还有更好的方法,如大家有兴趣可以联系我本人,方便更深一步的探讨。