मैं संग्रह और थ्रेडिंग के साथ खेल रहा हूं और निस्पटी एक्सटेंशन विधियों में आया हूं जो लोगों ने IDISposable पैटर्न की अनुमति देकर रीडरवाइटर लॉकस्लिम के उपयोग को कम करने के लिए बनाया है।रीडरवाइटर लॉकस्लिम एक्सटेंशन विधि प्रदर्शन
हालांकि, मेरा मानना है कि मुझे एहसास हुआ है कि कार्यान्वयन में कुछ प्रदर्शन प्रदर्शनकर्ता है। मुझे एहसास है कि एक्सटेंशन विधियों को वास्तव में प्रदर्शन को प्रभावित नहीं करना चाहिए, इसलिए मुझे लगता है कि कार्यान्वयन में कुछ कारण है ... डिस्पोजेबल structs की मात्रा बनाई/एकत्र की गई?
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
यहाँ एक विशिष्ट परिणाम है::
यहाँ कुछ परीक्षण कोड है
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
आप देख सकते हैं, एक्सटेंशन का उपयोग वास्तव में प्रदर्शन सिर्फ lock
(मॉनिटर) का उपयोग कर से भी बदतर बनाता है ।
कॉल करने के लिए निपटान अनलॉक करने के लिए भूल के समान है, और उस मामले में देरी भूलकर और गैर-निर्धारिती अंतिमकरण आने वाले मृत ताले को रोकने में मदद करने के लिए अत्यधिक संभावना नहीं है। इससे भी बदतर, यह संभव है (संभावित) कि यह मृत ताले को अंतराल और लगभग डीबग करने के लिए असंभव बना देगा। फाइनलर को छोड़ दें और सुनिश्चित करें कि हमें डिबग करने योग्य डेडलॉक मिल जाए। – wekempf
@wekempf देखें: http://gist.github.com/104477 साइड इफेक्ट यह है कि डिस्पोजेबल को कम से कम DEBUG –
@ sambo99 में काम करने के लिए एक वर्ग होना आवश्यक है: मैं वहां तर्क देख सकता हूं, लेकिन अभी भी नहीं इस बात से सहमत। सबसे अच्छा आप मृत लॉक को मृत लॉक में बदल दिया है + अंतिम कार्यवाही, बिना किसी अतिरिक्त लाभ के (यानी डीबग करना आसान नहीं है)। सब कुछ एक गलती के लिए जो मेरे लिए होने की संभावना नहीं है (विशेष रूप से यदि आप या तो विधियों के नाम बदलते हैं या उन्हें गैर-विस्तार विधियां बनाते हैं)। – wekempf