OSDN Git Service

ImageCacheクラスの排他制御にReaderWriterLockSlimを使用する
authorKimura Youichi <kim.upsilon@bucyou.net>
Sat, 28 May 2016 09:41:30 +0000 (18:41 +0900)
committerKimura Youichi <kim.upsilon@bucyou.net>
Sat, 28 May 2016 16:33:23 +0000 (01:33 +0900)
OpenTween/Extensions.cs
OpenTween/ImageCache.cs
OpenTween/OpenTween.csproj
OpenTween/ReaderWriterLockTransaction.cs [new file with mode: 0644]

index 6daef34..7d36d6a 100644 (file)
@@ -23,6 +23,7 @@ using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Text;
+using System.Threading;
 using System.Threading.Tasks;
 using System.Windows.Forms;
 
@@ -41,5 +42,14 @@ namespace OpenTween
 
             return selectedText;
         }
+
+        public static ReadLockTransaction BeginReadTransaction(this ReaderWriterLockSlim lockObj)
+            => new ReadLockTransaction(lockObj);
+
+        public static WriteLockTransaction BeginWriteTransaction(this ReaderWriterLockSlim lockObj)
+            => new WriteLockTransaction(lockObj);
+
+        public static UpgradeableReadLockTransaction BeginUpgradeableReadTransaction(this ReaderWriterLockSlim lockObj)
+            => new UpgradeableReadLockTransaction(lockObj);
     }
 }
index fff786f..3f60278 100644 (file)
@@ -47,7 +47,7 @@ namespace OpenTween
         /// <summary>
         /// innerDictionary の排他制御のためのロックオブジェクト
         /// </summary>
-        private object lockObject = new object();
+        private ReaderWriterLockSlim lockObject = new ReaderWriterLockSlim();
 
         /// <summary>
         /// オブジェクトが破棄された否か
@@ -90,28 +90,27 @@ namespace OpenTween
 
             return Task.Run(() =>
             {
-                Task<MemoryImage> cachedImageTask = null;
-                lock (this.lockObject)
+                if (force)
                 {
-                    innerDictionary.TryGetValue(address, out cachedImageTask);
+                    using (this.lockObject.BeginWriteTransaction())
+                        this.innerDictionary.Remove(address);
+                }
 
-                    if (cachedImageTask != null)
-                    {
-                        if (force)
-                        {
-                            this.innerDictionary.Remove(address);
-                            cachedImageTask = null;
-                        }
-                        else
-                            return cachedImageTask;
-                    }
+                using (var transaction = this.lockObject.BeginUpgradeableReadTransaction())
+                {
+                    Task<MemoryImage> cachedImageTask;
+                    if (innerDictionary.TryGetValue(address, out cachedImageTask))
+                        return cachedImageTask;
 
                     cancelToken.ThrowIfCancellationRequested();
 
-                    var imageTask = this.FetchImageAsync(address, cancelToken);
-                    this.innerDictionary[address] = imageTask;
+                    using (transaction.UpgradeToWriteLock())
+                    {
+                        var imageTask = this.FetchImageAsync(address, cancelToken);
+                        this.innerDictionary[address] = imageTask;
 
-                    return imageTask;
+                        return imageTask;
+                    }
                 }
             }, cancelToken);
         }
@@ -132,7 +131,7 @@ namespace OpenTween
 
         public MemoryImage TryGetFromCache(string address)
         {
-            lock (this.lockObject)
+            using (this.lockObject.BeginReadTransaction())
             {
                 Task<MemoryImage> imageTask;
                 if (!this.innerDictionary.TryGetValue(address, out imageTask) ||
@@ -145,7 +144,7 @@ namespace OpenTween
 
         public void CancelAsync()
         {
-            lock (this.lockObject)
+            using (this.lockObject.BeginWriteTransaction())
             {
                 var oldTokenSource = this.cancelTokenSource;
                 this.cancelTokenSource = new CancellationTokenSource();
@@ -163,7 +162,7 @@ namespace OpenTween
             {
                 this.CancelAsync();
 
-                lock (this.lockObject)
+                using (this.lockObject.BeginWriteTransaction())
                 {
                     foreach (var item in this.innerDictionary)
                     {
@@ -173,8 +172,10 @@ namespace OpenTween
                     }
 
                     this.innerDictionary.Clear();
-                    this.cancelTokenSource.Dispose();
                 }
+
+                this.cancelTokenSource.Dispose();
+                this.lockObject.Dispose();
             }
 
             this.disposed = true;
index 014b0d0..d53442d 100644 (file)
     <Compile Include="Models\UserTimelineTabModel.cs" />
     <Compile Include="MouseWheelMessageFilter.cs" />
     <Compile Include="NotifyPropertyChangedBase.cs" />
+    <Compile Include="ReaderWriterLockTransaction.cs" />
     <Compile Include="SendErrorReportForm.cs">
       <SubType>Form</SubType>
     </Compile>
diff --git a/OpenTween/ReaderWriterLockTransaction.cs b/OpenTween/ReaderWriterLockTransaction.cs
new file mode 100644 (file)
index 0000000..37b6869
--- /dev/null
@@ -0,0 +1,84 @@
+// OpenTween - Client of Twitter
+// Copyright (c) 2016 kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
+// All rights reserved.
+//
+// This file is part of OpenTween.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program. If not, see <http://www.gnu.org/licenses/>, or write to
+// the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
+// Boston, MA 02110-1301, USA.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace OpenTween
+{
+    public sealed class ReadLockTransaction : IDisposable
+    {
+        private readonly ReaderWriterLockSlim lockObj;
+
+        public ReadLockTransaction(ReaderWriterLockSlim lockObj)
+        {
+            if (lockObj == null)
+                throw new ArgumentNullException(nameof(lockObj));
+
+            this.lockObj = lockObj;
+            this.lockObj.EnterReadLock();
+        }
+
+        public void Dispose()
+            => this.lockObj.ExitReadLock();
+    }
+
+    public sealed class WriteLockTransaction : IDisposable
+    {
+        private readonly ReaderWriterLockSlim lockObj;
+
+        public WriteLockTransaction(ReaderWriterLockSlim lockObj)
+        {
+            if (lockObj == null)
+                throw new ArgumentNullException(nameof(lockObj));
+
+            this.lockObj = lockObj;
+            this.lockObj.EnterWriteLock();
+        }
+
+        public void Dispose()
+            => this.lockObj.ExitWriteLock();
+    }
+
+    public sealed class UpgradeableReadLockTransaction : IDisposable
+    {
+        private readonly ReaderWriterLockSlim lockObj;
+
+        public UpgradeableReadLockTransaction(ReaderWriterLockSlim lockObj)
+        {
+            if (lockObj == null)
+                throw new ArgumentNullException(nameof(lockObj));
+
+            this.lockObj = lockObj;
+            this.lockObj.EnterUpgradeableReadLock();
+        }
+
+        public WriteLockTransaction UpgradeToWriteLock()
+            => new WriteLockTransaction(this.lockObj);
+
+        public void Dispose()
+            => this.lockObj.ExitUpgradeableReadLock();
+    }
+}