私はコレクションとスレッドを使って遊んでいて、IDisposableパターンを許可してReaderWriterLockSlimの使用を容易にするために人々が作成したすばらしい拡張メソッドを見つけました。ReaderWriterLockSlim拡張メソッドパフォーマンス
しかし、実装の中の何かがパフォーマンスの犠牲者であることがわかったと思います。私は、拡張メソッドが実際にパフォーマンスに影響を及ぼさないと考えているので、実装の何かが原因であると仮定して残されています...作成/収集されたDisposable構造体の量?あなたが見ることができるように実際にパフォーマンスを行いだけlock
(モニター)を使用するよりも悪化し拡張子を使用して、
time: 00:00:03.0965242 class: System.Collections.Generic.List`1[System.Int32] time: 00:00:11.9194573 class: LockPlay.Program+MonitorList`1[System.Int32] time: 00:00:08.9510258 class: LockPlay.Program+RWLSList`1[System.Int32] time: 00:00:16.9888435 class: LockPlay.Program+RWLSExtList`1[System.Int32] DONE
:
はここにいくつかのテストコードです:
using System;
using System.Collections.Generic;
using System.Threading;
using System.Diagnostics;
namespace LockPlay {
static class RWLSExtension {
struct Disposable : IDisposable {
readonly Action _action;
public Disposable(Action action) {
_action = action;
}
public void Dispose() {
_action();
}
} // end struct
public static IDisposable ReadLock(this ReaderWriterLockSlim rwls) {
rwls.EnterReadLock();
return new Disposable(rwls.ExitReadLock);
}
public static IDisposable UpgradableReadLock(this ReaderWriterLockSlim rwls) {
rwls.EnterUpgradeableReadLock();
return new Disposable(rwls.ExitUpgradeableReadLock);
}
public static IDisposable WriteLock(this ReaderWriterLockSlim rwls) {
rwls.EnterWriteLock();
return new Disposable(rwls.ExitWriteLock);
}
} // end class
class Program {
class MonitorList<T> : List<T>, IList<T> {
object _syncLock = new object();
public MonitorList(IEnumerable<T> collection) : base(collection) { }
T IList<T>.this[int index] {
get {
lock(_syncLock)
return base[index];
}
set {
lock(_syncLock)
base[index] = value;
}
}
} // end class
class RWLSList<T> : List<T>, IList<T> {
ReaderWriterLockSlim _rwls = new ReaderWriterLockSlim();
public RWLSList(IEnumerable<T> collection) : base(collection) { }
T IList<T>.this[int index] {
get {
try {
_rwls.EnterReadLock();
return base[index];
} finally {
_rwls.ExitReadLock();
}
}
set {
try {
_rwls.EnterWriteLock();
base[index] = value;
} finally {
_rwls.ExitWriteLock();
}
}
}
} // end class
class RWLSExtList<T> : List<T>, IList<T> {
ReaderWriterLockSlim _rwls = new ReaderWriterLockSlim();
public RWLSExtList(IEnumerable<T> collection) : base(collection) { }
T IList<T>.this[int index] {
get {
using(_rwls.ReadLock())
return base[index];
}
set {
using(_rwls.WriteLock())
base[index] = value;
}
}
} // end class
static void Main(string[] args) {
const int ITERATIONS = 100;
const int WORK = 10000;
const int WRITE_THREADS = 4;
const int READ_THREADS = WRITE_THREADS * 3;
// create data - first List is for comparison only... not thread safe
int[] copy = new int[WORK];
IList<int>[] l = { new List<int>(copy), new MonitorList<int>(copy), new RWLSList<int>(copy), new RWLSExtList<int>(copy) };
// test each list
Thread[] writeThreads = new Thread[WRITE_THREADS];
Thread[] readThreads = new Thread[READ_THREADS];
foreach(var list in l) {
Stopwatch sw = Stopwatch.StartNew();
for(int k=0; k < ITERATIONS; k++) {
for(int i = 0; i < writeThreads.Length; i++) {
writeThreads[i] = new Thread(p => {
IList<int> il = p as IList<int>;
int c = il.Count;
for(int j = 0; j < c; j++) {
il[j] = j;
}
});
writeThreads[i].Start(list);
}
for(int i = 0; i < readThreads.Length; i++) {
readThreads[i] = new Thread(p => {
IList<int> il = p as IList<int>;
int c = il.Count;
for(int j = 0; j < c; j++) {
int temp = il[j];
}
});
readThreads[i].Start(list);
}
for(int i = 0; i < readThreads.Length; i++)
readThreads[i].Join();
for(int i = 0; i < writeThreads.Length; i++)
writeThreads[i].Join();
};
sw.Stop();
Console.WriteLine("time: {0} class: {1}", sw.Elapsed, list.GetType());
}
Console.WriteLine("DONE");
Console.ReadLine();
}
} // end class
} // end namespace
はここに典型的な結果です。
のDisposeを呼び出すことを忘れることはロックを解除し忘れると同一であり、その場合には遅れます非決定論的なファイナライズは、その後のデッドロックの防止にはあまり役立ちません。悪いことに、デッドロックを断続的にしてデバッグすることはほとんど不可能になる可能性があります(可能性があります)。ファイナライザをそのままにして、デバッグ可能なデッドロックを確保してください。 – wekempf
@wekempfを参照してください:http://gist.github.com/104477副作用は、これが少なくともDEBUG –
@ sambo99で動作するためには使い捨て可能なクラスが必要であるということです:私はそこに推論を見ることができますが、まだありません同意する。デッドロックを最終的なデッドロック+最終的なアサートに変えてしまいました。これは、メリットはありません(つまり、デバッグするのは簡単ではありません)。私にはほとんど起こりそうにない間違いがあります(特に、メソッドの名前を変更したり、メソッドを非拡張メソッドにするなど)。 – wekempf