Tôi đã chơi với các bộ sưu tập và luồng và đi qua các phương pháp mở rộng tiện lợi mà mọi người đã tạo ra để dễ dàng sử dụng ReaderWriterLockSlim bằng cách cho phép mẫu IDisposable.ReaderWriterLockSlim Extension Method Performance
Tuy nhiên, tôi tin rằng tôi đã nhận ra rằng điều gì đó trong quá trình triển khai là kẻ giết người hiệu suất. Tôi nhận ra rằng các phương pháp mở rộng không phải là thực sự ảnh hưởng đến hiệu suất, vì vậy tôi còn thừa nhận rằng một cái gì đó trong việc thực hiện là nguyên nhân ... số lượng các cấu trúc dùng một lần tạo ra/thu thập?
Dưới đây là một số mã kiểm tra:
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
Dưới đây là một kết quả tiêu biểu:
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
Như bạn thấy, sử dụng các phần mở rộng thực sự làm cho việc thực hiện XẤU HƠN hơn là chỉ sử dụng lock
(màn hình) .
Quên để gọi hàm Dispose là giống hệt nhau để quên để mở khóa, và trong trường hợp đó trì hoãn và quyết toán không xác định là rất khó để giúp ngăn chặn các khóa chết tiếp theo. Tồi tệ hơn, nó có thể (có thể xảy ra) rằng nó sẽ làm cho các khóa chết liên tục và gần như không thể gỡ lỗi. Để finalizer ra và đảm bảo chúng tôi có được một bế tắc có thể gỡ rối. – wekempf
@wekempf thấy: http://gist.github.com/104477 tác dụng phụ là nhu cầu dùng một lần phải là một lớp học để làm việc này ít nhất là trong DEBUG –
@ sambo99: Tôi có thể thấy lý do ở đó, nhưng vẫn không đồng ý. Tốt nhất bạn đã biến khóa chết thành khóa chết + xác nhận cuối cùng, không có lợi ích bổ sung (tức là không dễ gỡ lỗi hơn). Tất cả cho một sai lầm mà dường như HIGHLY dường như không xảy ra với tôi (đặc biệt là nếu bạn hoặc thay đổi tên của các phương pháp hoặc làm cho họ không mở rộng phương pháp). – wekempf