// 清除已经失效的 PageItem // parameters: // delta 闲置多少时间以上才清理 public void Clean(bool bLock, TimeSpan delta, Delegate_deleteFile procDeleteFile, CancellationToken token) { List <string> keys = new List <string>(); List <PageItem> items = new List <PageItem>(); if (bLock) { _lock.EnterReadLock(); } try { DateTime now = DateTime.Now; foreach (string key in _pageTable.Keys) { token.ThrowIfCancellationRequested(); PageItem item = _pageTable[key]; if (item.State == "OK" && now - item.LastUse > delta) { keys.Add(key); items.Add(item); } } } finally { if (bLock) { _lock.ExitReadLock(); } } if (keys.Count > 0) { if (bLock) { _lock.EnterWriteLock(); } try { foreach (string key in keys) { token.ThrowIfCancellationRequested(); _pageTable.Remove(key); } } finally { if (bLock) { _lock.ExitWriteLock(); } } foreach (PageItem item in items) { token.ThrowIfCancellationRequested(); if (procDeleteFile != null) { procDeleteFile(item.FilePath); } else { item.Delete(); } } } }
// 准备一个 PageItem 对象。如果在 _pageTable 中已经存在了,则直接利用它;如果没有,则新创建一个 // 但要处理好一个文件,新创建好的 PageItem 对象,其物理文件还没有来得及创建,如果这段时间又有另一个线程并发获得使用了这个对象,则会发生冲突。解决办法是,规定只返回准备好物理文件的 PageItem 对象。这样可能会出现两个线程都同时在创建相似属性的 PageItem 对象的可能 public PageItem GetPage(string object_recpath, int page_no, int dpi, string format, Delegate_prepareFile procPrepareFile, Delegate_deleteFile procDeleteFile, CancellationToken token) { string key = PageItem.MakeKey(object_recpath, page_no, dpi, format); PageItem item = null; bool bNew = false; // 是否新创建了 PageItem 对象 bool bFreely = false; // 是否为独立的 Page Item 对象。独立的 PageItem 对象不被集合管理 _lock.EnterWriteLock(); try { if (_pageTable.TryGetValue(key, out item) == true && item.State == "OK") { return(item); } if (item == null) { if (_pageTable.Count >= MAX_ITEMS) { // 紧急清理一次 Clean(false, TimeSpan.FromMilliseconds(0), procDeleteFile, token); } item = new PageItem { RecordPath = object_recpath, Page = page_no, DPI = dpi, Format = format, State = "Lock" }; if (this._pageTable.Count < MAX_ITEMS) { _pageTable.Add(key, item); } else { bFreely = true; } bNew = true; } else { // 等待状态变成 OK bNew = false; } } finally { _lock.ExitWriteLock(); } if (bNew == true) { try { // 创建物理文件 var ret = procPrepareFile(); item.FilePath = ret.Item1; item.TotalPage = ret.Item2; if (bFreely) { item.State = "Freely"; } else { item.State = "OK"; } // Thread.Sleep(10000); } catch { _lock.EnterWriteLock(); try { _pageTable.Remove(key); } finally { _lock.ExitWriteLock(); } throw; } } else { TimeSpan timeout = TimeSpan.FromSeconds(10); DateTime start = DateTime.Now; // 等待状态变为 OK while (true) { Thread.Sleep(100); if (item.State == "OK") { break; } // TODO: 超时要抛出异常 if (DateTime.Now - start >= timeout) { throw new TimeoutException("PageItem 对象被其它请求占用。请稍后重试"); } } } return(item); }