リビジョン | 46bd90540cf8daf4b90c5feeeb713dd63285692f (tree) |
---|---|
日時 | 2015-11-16 01:03:22 |
作者 | Kimura Youichi <kim.upsilon@bucy...> |
コミッター | Kimura Youichi |
TweenMainの発言一覧のキャッシュに対する処理をListViewItemCacheクラスにまとめる
Interlocked.Exchenge で新しいキャッシュと交換するようになったためロックは不要
@@ -191,11 +191,65 @@ namespace OpenTween | ||
191 | 191 | ////////////////////////////////////////////////////////////////////////////////////////////////////////// |
192 | 192 | private TabInformations _statuses; |
193 | 193 | |
194 | - // ListViewItem のキャッシュ関連 | |
195 | - private int _itemCacheIndex; | |
196 | - private ListViewItem[] _itemCache; | |
197 | - private PostClass[] _postCache; | |
198 | - private ReaderWriterLockSlim itemCacheLock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); | |
194 | + /// <summary> | |
195 | + /// 現在表示している発言一覧の <see cref="ListView"/> に対するキャッシュ | |
196 | + /// </summary> | |
197 | + /// <remarks> | |
198 | + /// キャッシュクリアのために null が代入されることがあるため、 | |
199 | + /// 使用する場合には <see cref="_listItemCache"/> に対して直接メソッド等を呼び出さずに | |
200 | + /// 一旦ローカル変数に代入してから参照すること。 | |
201 | + /// </remarks> | |
202 | + private ListViewItemCache _listItemCache = null; | |
203 | + | |
204 | + internal class ListViewItemCache | |
205 | + { | |
206 | + /// <summary>アイテムをキャッシュする対象の <see cref="ListView"/></summary> | |
207 | + public ListView TargetList { get; set; } | |
208 | + | |
209 | + /// <summary>キャッシュする範囲の開始インデックス</summary> | |
210 | + public int StartIndex { get; set; } | |
211 | + | |
212 | + /// <summary>キャッシュする範囲の終了インデックス</summary> | |
213 | + public int EndIndex { get; set; } | |
214 | + | |
215 | + /// <summary>キャッシュされた <see cref="ListViewItem"/> インスタンス</summary> | |
216 | + public ListViewItem[] ListItem { get; set; } | |
217 | + | |
218 | + /// <summary>キャッシュされた範囲に対応する <see cref="PostClass"/> インスタンス</summary> | |
219 | + public PostClass[] Post { get; set; } | |
220 | + | |
221 | + /// <summary>キャッシュされたアイテムの件数</summary> | |
222 | + public int Count | |
223 | + => this.EndIndex - this.StartIndex + 1; | |
224 | + | |
225 | + /// <summary>指定されたインデックスがキャッシュの範囲内であるか判定します</summary> | |
226 | + /// <returns><paramref name="index"/> がキャッシュの範囲内であれば true、それ以外は false</returns> | |
227 | + public bool Contains(int index) | |
228 | + => index >= this.StartIndex && index <= this.EndIndex; | |
229 | + | |
230 | + /// <summary>指定されたインデックスの範囲が全てキャッシュの範囲内であるか判定します</summary> | |
231 | + /// <returns><paramref name="rangeStart"/> から <paramref name="rangeEnd"/> の範囲が全てキャッシュの範囲内であれば true、それ以外は false</returns> | |
232 | + public bool IsSupersetOf(int rangeStart, int rangeEnd) | |
233 | + => rangeStart >= this.StartIndex && rangeEnd <= this.EndIndex; | |
234 | + | |
235 | + /// <summary>指定されたインデックスの <see cref="ListViewItem"/> と <see cref="PostClass"/> をキャッシュから取得することを試みます</summary> | |
236 | + /// <returns>取得に成功すれば true、それ以外は false</returns> | |
237 | + public bool TryGetValue(int index, out ListViewItem item, out PostClass post) | |
238 | + { | |
239 | + if (this.Contains(index)) | |
240 | + { | |
241 | + item = this.ListItem[index - this.StartIndex]; | |
242 | + post = this.Post[index - this.StartIndex]; | |
243 | + return true; | |
244 | + } | |
245 | + else | |
246 | + { | |
247 | + item = null; | |
248 | + post = null; | |
249 | + return false; | |
250 | + } | |
251 | + } | |
252 | + } | |
199 | 253 | |
200 | 254 | private TabPage _curTab; |
201 | 255 | private int _curItemIndex; |
@@ -342,7 +396,6 @@ namespace OpenTween | ||
342 | 396 | |
343 | 397 | this.thumbnailTokenSource?.Dispose(); |
344 | 398 | |
345 | - this.itemCacheLock.Dispose(); | |
346 | 399 | this.tw.Dispose(); |
347 | 400 | this._hookGlobalHotkey.Dispose(); |
348 | 401 | } |
@@ -1900,14 +1953,14 @@ namespace OpenTween | ||
1900 | 1953 | if (!tabInfo.UnreadManage || |
1901 | 1954 | !this._cfgCommon.UnreadManage) Read = true; |
1902 | 1955 | |
1903 | - //対象の特定 | |
1904 | - ListViewItem itm = null; | |
1905 | - PostClass post = null; | |
1906 | - | |
1907 | - this.TryGetListViewItemCache(Index, out itm, out post); | |
1956 | + var listCache = this._listItemCache; | |
1957 | + if (listCache == null) | |
1958 | + return; | |
1908 | 1959 | |
1909 | 1960 | // キャッシュに含まれていないアイテムは対象外 |
1910 | - if (itm == null) | |
1961 | + ListViewItem itm; | |
1962 | + PostClass post; | |
1963 | + if (!listCache.TryGetValue(Index, out itm, out post)) | |
1911 | 1964 | return; |
1912 | 1965 | |
1913 | 1966 | ChangeItemStyleRead(Read, itm, post, ((DetailsListView)_curTab.Tag)); |
@@ -1969,30 +2022,15 @@ namespace OpenTween | ||
1969 | 2022 | |
1970 | 2023 | if (_post == null) return; |
1971 | 2024 | |
1972 | - var itemColors = new Color[] { }; | |
1973 | - int itemIndex = -1; | |
1974 | - | |
1975 | - this.itemCacheLock.EnterReadLock(); | |
1976 | - try | |
1977 | - { | |
1978 | - if (this._itemCache == null) return; | |
1979 | - | |
1980 | - var query = | |
1981 | - from i in Enumerable.Range(0, this._itemCache.Length) | |
1982 | - select this.JudgeColor(_post, this._postCache[i]); | |
1983 | - | |
1984 | - itemColors = query.ToArray(); | |
1985 | - itemIndex = _itemCacheIndex; | |
1986 | - } | |
1987 | - finally { this.itemCacheLock.ExitReadLock(); } | |
1988 | - | |
1989 | - if (itemIndex < 0) return; | |
2025 | + var listCache = this._listItemCache; | |
2026 | + if (listCache == null) | |
2027 | + return; | |
1990 | 2028 | |
1991 | - foreach (var backColor in itemColors) | |
2029 | + var index = listCache.StartIndex; | |
2030 | + foreach (var cachedPost in listCache.Post) | |
1992 | 2031 | { |
1993 | - // この処理中に MyList_CacheVirtualItems が呼ばれることがあるため、 | |
1994 | - // 同一スレッド内での二重ロックを避けるためにロックの外で実行する必要がある | |
1995 | - _curList.ChangeItemBackColor(itemIndex++, backColor); | |
2032 | + var backColor = this.JudgeColor(_post, cachedPost); | |
2033 | + this._curList.ChangeItemBackColor(index++, backColor); | |
1996 | 2034 | } |
1997 | 2035 | } |
1998 | 2036 |
@@ -3516,18 +3554,18 @@ namespace OpenTween | ||
3516 | 3554 | |
3517 | 3555 | private PostClass GetCurTabPost(int Index) |
3518 | 3556 | { |
3519 | - this.itemCacheLock.EnterReadLock(); | |
3520 | - try | |
3557 | + var listCache = this._listItemCache; | |
3558 | + if (listCache != null) | |
3521 | 3559 | { |
3522 | - if (_postCache != null && Index >= _itemCacheIndex && Index < _itemCacheIndex + _postCache.Length) | |
3523 | - return _postCache[Index - _itemCacheIndex]; | |
3560 | + ListViewItem item; | |
3561 | + PostClass post; | |
3562 | + if (listCache.TryGetValue(Index, out item, out post)) | |
3563 | + return post; | |
3524 | 3564 | } |
3525 | - finally { this.itemCacheLock.ExitReadLock(); } | |
3526 | 3565 | |
3527 | 3566 | return _statuses.Tabs[_curTab.Text][Index]; |
3528 | 3567 | } |
3529 | 3568 | |
3530 | - | |
3531 | 3569 | private async void MoveToHomeToolStripMenuItem_Click(object sender, EventArgs e) |
3532 | 3570 | { |
3533 | 3571 | if (_curList.SelectedIndices.Count > 0) |
@@ -5233,85 +5271,74 @@ namespace OpenTween | ||
5233 | 5271 | |
5234 | 5272 | private void MyList_CacheVirtualItems(object sender, CacheVirtualItemsEventArgs e) |
5235 | 5273 | { |
5236 | - this.itemCacheLock.EnterUpgradeableReadLock(); | |
5237 | - try | |
5238 | - { | |
5239 | - if (_curList.Equals(sender)) | |
5240 | - { | |
5241 | - if (_itemCache != null && | |
5242 | - e.StartIndex >= _itemCacheIndex && | |
5243 | - e.EndIndex < _itemCacheIndex + _itemCache.Length) | |
5244 | - { | |
5245 | - //If the newly requested cache is a subset of the old cache, | |
5246 | - //no need to rebuild everything, so do nothing. | |
5247 | - return; | |
5248 | - } | |
5274 | + if (sender != this._curList) | |
5275 | + return; | |
5249 | 5276 | |
5250 | - //Now we need to rebuild the cache. | |
5251 | - CreateCache(e.StartIndex, e.EndIndex); | |
5252 | - } | |
5277 | + var listCache = this._listItemCache; | |
5278 | + if (listCache != null && listCache.IsSupersetOf(e.StartIndex, e.EndIndex)) | |
5279 | + { | |
5280 | + // If the newly requested cache is a subset of the old cache, | |
5281 | + // no need to rebuild everything, so do nothing. | |
5282 | + return; | |
5253 | 5283 | } |
5254 | - finally { this.itemCacheLock.ExitUpgradeableReadLock(); } | |
5284 | + | |
5285 | + // Now we need to rebuild the cache. | |
5286 | + this.CreateCache(e.StartIndex, e.EndIndex); | |
5255 | 5287 | } |
5256 | 5288 | |
5257 | 5289 | private void MyList_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e) |
5258 | 5290 | { |
5259 | - ListViewItem item = null; | |
5260 | - PostClass cacheItemPost = null; | |
5261 | - | |
5262 | - if (_curList.Equals(sender)) | |
5263 | - this.TryGetListViewItemCache(e.ItemIndex, out item, out cacheItemPost); | |
5264 | - | |
5265 | - if (item == null) | |
5291 | + var listCache = this._listItemCache; | |
5292 | + if (listCache != null && listCache.TargetList == sender) | |
5266 | 5293 | { |
5267 | - //A cache miss, so create a new ListViewItem and pass it back. | |
5268 | - TabPage tb = (TabPage)((DetailsListView)sender).Parent; | |
5269 | - try | |
5294 | + ListViewItem item; | |
5295 | + PostClass cacheItemPost; | |
5296 | + if (listCache.TryGetValue(e.ItemIndex, out item, out cacheItemPost)) | |
5270 | 5297 | { |
5271 | - item = this.CreateItem(tb, _statuses.Tabs[tb.Text][e.ItemIndex], e.ItemIndex); | |
5272 | - } | |
5273 | - catch (Exception) | |
5274 | - { | |
5275 | - //不正な要求に対する間に合わせの応答 | |
5276 | - string[] sitem = {"", "", "", "", "", "", "", ""}; | |
5277 | - item = new ImageListViewItem(sitem); | |
5298 | + e.Item = item; | |
5299 | + return; | |
5278 | 5300 | } |
5279 | 5301 | } |
5280 | 5302 | |
5281 | - e.Item = item; | |
5303 | + // A cache miss, so create a new ListViewItem and pass it back. | |
5304 | + TabPage tb = (TabPage)((DetailsListView)sender).Parent; | |
5305 | + try | |
5306 | + { | |
5307 | + e.Item = this.CreateItem(tb, _statuses.Tabs[tb.Text][e.ItemIndex], e.ItemIndex); | |
5308 | + } | |
5309 | + catch (Exception) | |
5310 | + { | |
5311 | + // 不正な要求に対する間に合わせの応答 | |
5312 | + string[] sitem = {"", "", "", "", "", "", "", ""}; | |
5313 | + e.Item = new ImageListViewItem(sitem); | |
5314 | + } | |
5282 | 5315 | } |
5283 | 5316 | |
5284 | - private void CreateCache(int StartIndex, int EndIndex) | |
5317 | + private void CreateCache(int startIndex, int endIndex) | |
5285 | 5318 | { |
5286 | - this.itemCacheLock.EnterWriteLock(); | |
5287 | - try | |
5288 | - { | |
5289 | - var tabInfo = _statuses.Tabs[_curTab.Text]; | |
5319 | + var tabInfo = this._statuses.Tabs[this._curTab.Text]; | |
5290 | 5320 | |
5291 | - //キャッシュ要求(要求範囲±30を作成) | |
5292 | - StartIndex -= 30; | |
5293 | - if (StartIndex < 0) StartIndex = 0; | |
5294 | - EndIndex += 30; | |
5295 | - if (EndIndex >= tabInfo.AllCount) EndIndex = tabInfo.AllCount - 1; | |
5296 | - _postCache = tabInfo[StartIndex, EndIndex]; //配列で取得 | |
5297 | - _itemCacheIndex = StartIndex; | |
5321 | + // キャッシュ要求(要求範囲±30を作成) | |
5322 | + startIndex = Math.Max(startIndex - 30, 0); | |
5323 | + endIndex = Math.Min(endIndex + 30, tabInfo.AllCount - 1); | |
5298 | 5324 | |
5299 | - _itemCache = new ListViewItem[0] {}; | |
5300 | - Array.Resize(ref _itemCache, _postCache.Length); | |
5325 | + var cacheLength = endIndex - startIndex + 1; | |
5301 | 5326 | |
5302 | - for (int i = 0; i < _postCache.Length; i++) | |
5303 | - { | |
5304 | - _itemCache[i] = CreateItem(_curTab, _postCache[i], StartIndex + i); | |
5305 | - } | |
5306 | - } | |
5307 | - catch (Exception) | |
5327 | + var posts = tabInfo[startIndex, endIndex]; //配列で取得 | |
5328 | + var listItems = Enumerable.Range(0, cacheLength) | |
5329 | + .Select(x => this.CreateItem(this._curTab, posts[x], startIndex + x)) | |
5330 | + .ToArray(); | |
5331 | + | |
5332 | + var listCache = new ListViewItemCache | |
5308 | 5333 | { |
5309 | - //キャッシュ要求が実データとずれるため(イベントの遅延?) | |
5310 | - _postCache = null; | |
5311 | - _itemCacheIndex = -1; | |
5312 | - _itemCache = null; | |
5313 | - } | |
5314 | - finally { this.itemCacheLock.ExitWriteLock(); } | |
5334 | + TargetList = this._curList, | |
5335 | + StartIndex = startIndex, | |
5336 | + EndIndex = endIndex, | |
5337 | + Post = posts, | |
5338 | + ListItem = listItems, | |
5339 | + }; | |
5340 | + | |
5341 | + Interlocked.Exchange(ref this._listItemCache, listCache); | |
5315 | 5342 | } |
5316 | 5343 | |
5317 | 5344 | /// <summary> |
@@ -5319,33 +5346,7 @@ namespace OpenTween | ||
5319 | 5346 | /// </summary> |
5320 | 5347 | private void PurgeListViewItemCache() |
5321 | 5348 | { |
5322 | - this.itemCacheLock.EnterWriteLock(); | |
5323 | - try | |
5324 | - { | |
5325 | - this._itemCache = null; | |
5326 | - this._itemCacheIndex = -1; | |
5327 | - this._postCache = null; | |
5328 | - } | |
5329 | - finally { this.itemCacheLock.ExitWriteLock(); } | |
5330 | - } | |
5331 | - | |
5332 | - private bool TryGetListViewItemCache(int index, out ListViewItem item, out PostClass post) | |
5333 | - { | |
5334 | - this.itemCacheLock.EnterReadLock(); | |
5335 | - try | |
5336 | - { | |
5337 | - if (this._itemCache != null && index >= this._itemCacheIndex && index < this._itemCacheIndex + this._itemCache.Length) | |
5338 | - { | |
5339 | - item = this._itemCache[index - _itemCacheIndex]; | |
5340 | - post = this._postCache[index - _itemCacheIndex]; | |
5341 | - return true; | |
5342 | - } | |
5343 | - } | |
5344 | - finally { this.itemCacheLock.ExitReadLock(); } | |
5345 | - | |
5346 | - item = null; | |
5347 | - post = null; | |
5348 | - return false; | |
5349 | + Interlocked.Exchange(ref this._listItemCache, null); | |
5349 | 5350 | } |
5350 | 5351 | |
5351 | 5352 | private ListViewItem CreateItem(TabPage Tab, PostClass Post, int Index) |