|
這段時(shí)間,我正在負(fù)責(zé)升級(jí)公司中一個(gè)產(chǎn)品的幾個(gè)版本。這幾個(gè)系統(tǒng)的基本框架都相同,架構(gòu)都是以第一個(gè)系統(tǒng)為模型,只是后續(xù)的產(chǎn)品擴(kuò)展了許多功能。總的說(shuō)來(lái)這幾個(gè)產(chǎn)品邏輯也不是很復(fù)雜,但因幾易開發(fā)員,而且沒有標(biāo)準(zhǔn)和缺乏設(shè)計(jì),所以系統(tǒng)的可維護(hù)性較低。
上個(gè)星期,我重構(gòu)了產(chǎn)品中的文件操作方案。原有的操作方案是先以讀的方式打開原文件,而后從該文件中不斷地讀取指定緩沖大小的內(nèi)容,并對(duì)讀取的字串進(jìn)行處理,把處理過(guò)后的內(nèi)容寫入到一個(gè)臨時(shí)文件中,最后把原文件刪除而把臨時(shí)文件更名為原文件名。個(gè)人基于如下的考慮認(rèn)為這種方案不適于當(dāng)前場(chǎng)景:(1)需修改一條內(nèi)容時(shí),得從文件頭一條一條地讀取出來(lái),然后判斷是修改或者是刪除,或者是沒有找到則是增加。(2)這種方案對(duì)于刪除、修改操作都要先逐條地移動(dòng)記錄到臨時(shí)文件,而后再重命名,所以這種方案對(duì)于大文件或者是經(jīng)常修改的文件是不合適的。(3)由于這塊文件操作是多線程的,那么可以假設(shè)一個(gè)場(chǎng)景 ,TA線程以共享讀的方式打開了文件,然后轉(zhuǎn)化到了臨時(shí)文件,關(guān)閉兩個(gè)文件句柄,TA停止。這時(shí)TB也打開原有文件并讀取了一段內(nèi)容,并打開重寫了臨時(shí)文件,接著TB又停止并關(guān)閉了文件句柄。TA繼續(xù)工作,刪除原文件重命名臨時(shí)文件,這時(shí)TB線程讀了臟數(shù)據(jù)而TA就有可能提交了錯(cuò)誤的數(shù)據(jù)。當(dāng)然這種情況是我推理出來(lái)的,實(shí)際是很少出現(xiàn)的,問(wèn)題肯定是存在的。有關(guān)的這種文件操作方式的其它缺點(diǎn)還可以查閱windows核心編程中的相關(guān)章節(jié)。其中也有相關(guān)的論述?;谏鲜龅姆治?,我決定重構(gòu)其實(shí)現(xiàn)。我提出了兩種備選方案,(1)采用XML的方式,對(duì)于文件鎖定的方案是以流的方式從獨(dú)占的文件中讀取,完成操作寫入時(shí)關(guān)閉。采用這種方式有利于查找、修改、刪除和添加。因?yàn)?/span>XML文件要先載入內(nèi)存并解析成DOM樹,所以XML文件較大時(shí)就不適合。(2)以獨(dú)占方式打開文件,采用內(nèi)存映射的方式操作,完成操作后寫回并關(guān)閉。在創(chuàng)建映射內(nèi)核對(duì)象時(shí)以原文件大小再加一個(gè)適當(dāng)?shù)脑隽?,最后根?jù)實(shí)際的大小截?cái)辔募Mㄟ^(guò)比較這兩種方案在執(zhí)行50000條記錄時(shí),都是1500ms這內(nèi)。最后,基于與原有格式的兼容性和可能會(huì)出現(xiàn)大文件的考慮,我選擇了內(nèi)存映射方式。通過(guò)此次重構(gòu),大大提高了系統(tǒng)的運(yùn)行效率和穩(wěn)定性。
代碼如下,供大家點(diǎn)評(píng)。
1 //頭文件 2 #pragma once 3 4 class ShadowDirText 5  { 6 public: 7 ShadowDirText(LPCTSTR szId); 8 virtual ~ShadowDirText(void); 9 10 private: 11 ShadowDirText(const ShadowDirText &); 12 ShadowDirText& operator=(const ShadowDirText &); 13 14 public: 15 static bool GetDirShadowListFilePath(LPCTSTR szDirPath, CString &strPath); 16 static bool Exists(LPCTSTR szDirPath); 17 static bool Create(LPCTSTR szDirPath); 18 static bool Delete(LPCTSTR szDirPath); 19 static void SetFileName(LPCTSTR szName); 20 21 bool Open(LPCTSTR szDirPath); 22 void Close(); 23 bool RemoveRecord(LPCTSTR szPath, bool bDir); 24 bool AddRecord(LPCTSTR szPath, bool bDir); 25 bool ExistRecord(LPCTSTR szPath, bool bDir); 26 27 bool RemoveAll(); 28 29 private: 30 DWORD Find(LPCTSTR szPath, bool bDir); 31 bool FindLineById(DWORD dwSearchPosition, DWORD &dwBegin, DWORD &dwEnd); 32 void FillEnd(); 33 34 private: 35 CString m_strId; 36 CString m_strDir; 37 HANDLE m_hFile; 38 HANDLE m_hFileMapping; 39 PVOID m_pvMapView; 40 DWORD m_dwFileSize; 41 DWORD m_dwOriginalSize; 42 43 static const int m_snExtened; 44 static TCHAR m_sszFileName[_MAX_FNAME]; 45 static const CString m_sstrFormatting; 46 static const TCHAR m_sszLineEnd[2]; 47 }; 48
1 #include "StdAfx.h" 2 #include "ShadowDirText.h" 3 4 /**/////////////////////////////////////////////////////////////////////////// 5 // Constraint and static variable 6 // 7 // 8 /**/////////////////////////////////////////////////////////////////////////// 9 const int ShadowDirText::m_snExtened =102400; //100KB 10 TCHAR ShadowDirText::m_sszFileName[_MAX_FNAME] = {_T('\0')}; 11 12 // attention: 13 // m_sstrFormatting and m_sszLineEnd must have the same end symbols. 14 // 15 const CString ShadowDirText::m_sstrFormatting = _T("id=\"%s\",name=\"%s\",isdir=\"%d\"\r\n"); 16 const TCHAR ShadowDirText::m_sszLineEnd[2] = {_T('\r'), _T('\n')}; 17 18 /**/////////////////////////////////////////////////////////////////////////// 19 // Constructor and Destructor 20 // 21 // 22 /**/////////////////////////////////////////////////////////////////////////// 23 ShadowDirText::ShadowDirText(LPCTSTR szId) 24 : m_strId(szId) 25  { 26 m_hFile = NULL; 27 m_hFileMapping = NULL; 28 m_pvMapView = NULL; 29 m_dwFileSize = 0; 30 } 31 32 ShadowDirText::~ShadowDirText(void) 33  { 34 Close(); 35 } 36 37 /**/////////////////////////////////////////////////////////////////////////// 38 // public static methods 39 // 40 // 41 /**/////////////////////////////////////////////////////////////////////////// 42 43 bool ShadowDirText::GetDirShadowListFilePath(LPCTSTR szDirPath, CString &strPath) 44  { 45 ATLASSERT(szDirPath && 46 _T("argument 'szDirPath' of ShadowDirListHelper::GetDirShadowListFilePath is null.")); 47 48 strPath = szDirPath; 49 if(strPath.GetLength() == 0) 50 { 51 return false; 52 } 53 if(strPath.GetAt(strPath.GetLength() -1) != _T('\\')) 54 { 55 strPath.AppendChar(_T('\\')); 56 } 57 if(!PathFileExists(strPath)) 58 { 59 return false; 60 } 61 strPath.Append(m_sszFileName); 62 return true; 63 } 64 65 bool ShadowDirText::Exists( LPCTSTR szDirPath ) 66  { 67 CString strPath; 68 if(!GetDirShadowListFilePath(szDirPath, strPath)) 69 { 70 return false; 71 } 72 if(!PathFileExists(strPath)) 73 { 74 return false; 75 } 76 return true; 77 } 78 79 bool ShadowDirText::Create(LPCTSTR szDirPath) 80  { 81 CString strPath; 82 if(!GetDirShadowListFilePath(szDirPath, strPath)) 83 { 84 return false; 85 } 86 if(!PathFileExists(strPath)) 87 { 88 HANDLE handle = CreateFile(strPath, 89 GENERIC_READ | GENERIC_WRITE, 90 0, 91 NULL, 92 OPEN_ALWAYS, 93 FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_ARCHIVE, 94 NULL); 95 if(INVALID_HANDLE_VALUE == handle) 96 { 97 return false; 98 } 99 static BYTE unicodeHeader[]= {0xff, 0xfe}; 100 static TCHAR szEnd[] = _T("\r\n"); 101 DWORD dwWrited = 0; 102 WriteFile(handle, unicodeHeader, sizeof(unicodeHeader), &dwWrited,0); 103 WriteFile(handle,szDirPath, (DWORD)(_tcslen(szDirPath) * sizeof(TCHAR)), &dwWrited, 0); 104 WriteFile(handle, szEnd, (DWORD)(_tcslen(szEnd) * sizeof(TCHAR)), &dwWrited, 0); 105 CloseHandle(handle); 106 } 107 return true; 108 } 109 110 bool ShadowDirText::Delete( LPCTSTR szDirPath ) 111  { 112 CString strPath; 113 if(!GetDirShadowListFilePath(szDirPath, strPath)) 114 { 115 return true; 116 } 117 if(PathFileExists(strPath)) 118 { 119 if(!DeleteFile(strPath)) 120 { 121 return false; 122 } 123 } 124 return true; 125 } 126 127 void ShadowDirText::SetFileName( LPCTSTR szName ) 128  { 129 ATLASSERT(szName && _T("argument of ShadowDirListHelper::SetFileName is NULL")); 130 _tcscpy_s(m_sszFileName,_MAX_FNAME, szName); 131 } 132 /**///////////////////////////////////////////////////////////////////////////133 // public methods 134 // 135 // 136 /**///////////////////////////////////////////////////////////////////////////137 bool ShadowDirText::Open(LPCTSTR szDirPath ) 138  { 139 CString strPath; 140 if(!GetDirShadowListFilePath(szDirPath, strPath)) 141 { 142 return false; 143 } 144 if(!PathFileExists(strPath)) 145 { 146 if(!Create(szDirPath)) 147 { 148 return false; 149 } 150 } 151 Close(); 152 m_strDir = szDirPath; 153 m_hFile = CreateFile(strPath, 154 GENERIC_READ | GENERIC_WRITE, 155 0, 156 NULL, 157 OPEN_EXISTING, 158 FILE_ATTRIBUTE_HIDDEN, 159 0); 160 if(INVALID_HANDLE_VALUE == m_hFile) 161 { 162 return false; 163 } 164 m_dwOriginalSize = m_dwFileSize = GetFileSize(m_hFile, NULL); 165 m_hFileMapping = CreateFileMapping(m_hFile, 166 NULL, 167 PAGE_READWRITE, 168 0, 169 m_dwFileSize + m_snExtened, 170 0); 171 if(NULL == m_hFileMapping) 172 { 173 Close(); 174 return false; 175 } 176 m_pvMapView = MapViewOfFile(m_hFileMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0); 177 if(NULL == m_pvMapView) 178 { 179 Close(); 180 return false; 181 } 182 m_pvMapView = (char*)m_pvMapView + 2; 183 m_dwFileSize -= 2; 184 FillEnd(); 185 return true; 186 } 187 188 void ShadowDirText::Close() 189  { 190 if(m_pvMapView) 191 { 192 m_pvMapView = (char*)m_pvMapView - 2; 193 ::UnmapViewOfFile(m_pvMapView); 194 m_pvMapView = NULL; 195 } 196 if(m_hFileMapping) 197 { 198 CloseHandle(m_hFileMapping); 199 m_hFileMapping = NULL; 200 } 201 if(m_hFile) 202 { 203 SetFilePointer(m_hFile, m_dwFileSize + 2, NULL, FILE_BEGIN); 204 SetEndOfFile(m_hFile); 205 CloseHandle(m_hFile); 206 m_hFile = NULL; 207 } 208 } 209 210 bool ShadowDirText::RemoveRecord( LPCTSTR szPath, bool bDir) 211  { 212 DWORD dwPosition = Find(szPath, bDir); 213 if(0 == dwPosition) 214 { 215 return true; 216 } 217 CString strTemp; 218 strTemp.Format(m_sstrFormatting, m_strId, szPath, (bDir? 1: 0)); 219 int nLength = strTemp.GetLength() * sizeof(TCHAR); 220 char *pBegin = (char*)m_pvMapView; 221 memmove_s(pBegin + dwPosition, m_dwFileSize - dwPosition,pBegin + dwPosition + nLength, 222 m_dwFileSize - dwPosition); 223 m_dwFileSize -= nLength; 224 FillEnd(); 225 return true; 226 } 227 228 bool ShadowDirText::AddRecord(LPCTSTR szPath, bool bDir) 229  { 230 DWORD dwPosition = Find(szPath, bDir); 231 if(0 != dwPosition) 232 { 233 return true; 234 } 235 CString strTemp; 236 strTemp.Format(m_sstrFormatting, m_strId, szPath, (bDir? 1: 0)); 237 int nLength = strTemp.GetLength() * sizeof(TCHAR); 238 if(m_dwFileSize + nLength + 2 > m_dwOriginalSize + m_snExtened) 239 { 240 Close(); 241 Open(m_strDir); 242 return AddRecord(szPath, bDir); 243 } 244 char *pBegin = (char*)m_pvMapView; 245 TCHAR *pWriten=(TCHAR*)(pBegin + m_dwFileSize); 246 memcpy_s(pWriten, nLength + sizeof(TCHAR), strTemp, nLength); 247 m_dwFileSize += nLength; 248 FillEnd(); 249 return true; 250 } 251 252 bool ShadowDirText::ExistRecord( LPCTSTR szPath, bool bDir) 253  { 254 return (Find(szPath, bDir)? true: false); 255 } 256 257 bool ShadowDirText::RemoveAll() 258  { 259 DWORD dwSearchPosition = 0; 260 DWORD dwBegin = 0; 261 DWORD dwEnd = 0; 262 bool bRet = true; 263 while (FindLineById(dwSearchPosition, dwBegin, dwEnd)) 264 { 265 if(dwEnd > dwBegin) 266 { 267 char *pBegin = (char*)m_pvMapView; 268 memmove_s(pBegin + dwBegin, m_dwFileSize - dwBegin,pBegin + dwEnd, 269 m_dwFileSize - dwBegin); 270 m_dwFileSize -= (dwEnd - dwBegin); 271 FillEnd(); 272 } 273 } 274 if (dwBegin >= m_dwFileSize || dwEnd > m_dwFileSize) 275 { 276 bRet = false; 277 } 278 return bRet; 279 } 280 /**///////////////////////////////////////////////////////////////////////////281 // private methods 282 // 283 // 284 /**///////////////////////////////////////////////////////////////////////////285 DWORD ShadowDirText::Find( LPCTSTR szPath, bool bDir ) 286  { 287 ATLASSERT(m_pvMapView != NULL && _T("Map file base address is invalid.")); 288 if(NULL == m_pvMapView) 289 { 290 return 0; 291 } 292 CString strTemp; 293 strTemp.Format(m_sstrFormatting, m_strId, szPath, (bDir? 1: 0)); 294 TCHAR *pBegin =(TCHAR*)m_pvMapView; 295 TCHAR *p = _tcsstr(pBegin, strTemp); 296 if(!p) 297 { 298 return 0; 299 } 300 DWORD dwOffset = (DWORD)(p - pBegin) * sizeof(TCHAR); 301 if(dwOffset >= m_dwFileSize) 302 { 303 return 0; 304 } 305 return dwOffset; 306 } 307 308 void ShadowDirText::FillEnd() 309  { 310 char *pBegin = (char*)m_pvMapView; 311 TCHAR *pWriten=(TCHAR*)(pBegin + m_dwFileSize); 312 *pWriten = 0; 313 } 314 315 bool ShadowDirText::FindLineById( DWORD dwSearchPosition, DWORD &dwBegin, DWORD &dwEnd ) 316  { 317 ATLASSERT(dwSearchPosition % sizeof(TCHAR) == 0 && 318 _T("argument \"dwSearchPosition\" of ShadowDirText::FindLineById must be even")); 319 dwBegin = 0; 320 dwEnd = 0; 321 if(dwSearchPosition % sizeof(TCHAR)) 322 { 323 return true; 324 } 325 CString str; 326 str.Format(_T("id=\"%s\""), m_strId); 327 TCHAR *pBegin = (TCHAR *)((char*)m_pvMapView + dwSearchPosition); 328 TCHAR *p1 = _tcsstr(pBegin, str); 329 if(!p1) 330 { 331 return false; 332 } 333 dwBegin = dwSearchPosition + (p1 - pBegin) * sizeof(TCHAR); 334 TCHAR *p2 = _tcsstr(p1, m_sszLineEnd); 335 if(!p2) 336 { 337 dwEnd = m_dwFileSize; 338 } 339 else 340 { 341 dwEnd = dwBegin + (p2 - p1) * sizeof(TCHAR) + sizeof(m_sszLineEnd); 342 } 343 if(dwBegin >= m_dwFileSize || dwEnd > m_dwFileSize) 344 { 345 ATLASSERT(_T("the end of memory was bad.")); 346 return false; 347 } 348 return true; 349 }
由于篇幅太長(zhǎng),我就不貼上XML文件操作的封裝類。在上面的代碼中str.Format(_T("id=\"%s\""), m_strId);這語(yǔ)句應(yīng)提取出來(lái)以提高可維護(hù)性。這里貼出的是評(píng)價(jià)方案時(shí)的類,并不是實(shí)際項(xiàng)目中的類。但總體上是一樣的,差別是完成了id, name,isdir等的常量及相關(guān)格式的標(biāo)準(zhǔn)化。
|