gpt4 book ai didi

c# - ReaderWriterLockSlim 扩展方法性能

转载 作者:太空狗 更新时间:2023-10-29 18:11:34 26 4
gpt4 key购买 nike

我一直在研究集合和线程,并发现了人们创建的漂亮的扩展方法,它们通过允许 IDisposable 模式来简化 ReaderWriterLockSlim 的使用。

但是,我相信我已经意识到实现中的某些东西是性能 killer 。我意识到扩展方法不应该真正影响性能,所以我假设实现中的某些东西是原因......创建/收集的一次性结构的数量?

下面是一些测试代码:

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(监视器)相比,使用扩展实际上使性能更差

最佳答案

看起来这是实例化数百万个结构和额外调用的代价。

我什至会说 ReaderWriterLockSlim 在这个示例中被滥用了,在这种情况下锁就足够了,与向初级解释这些概念的价格相比,你使用 ReaderWriterLockSlim 获得的性能优势可以忽略不计开发者。

当需要花费不可忽略的时间来执行读取和写入时,您可以使用读写器样式锁获得巨大的优势。当您拥有一个主要基于读取的系统时,提升将是最大的。

尝试在获取锁时插入 Thread.Sleep(1) 以查看它产生的差异有多大。

查看此基准:

Time for Test.SynchronizedList`1[System.Int32] Time Elapsed 12310 msTime for Test.ReaderWriterLockedList`1[System.Int32] Time Elapsed 547 msTime for Test.ManualReaderWriterLockedList`1[System.Int32] Time Elapsed 566 ms

In my benchmarking I do not really notice much of a difference between the two styles, I would feel comfortable using it provided it had some finalizer protection in case people forget to dispose ....

using System.Threading;
using System.Diagnostics;
using System.Collections.Generic;
using System;
using System.Linq;

namespace Test {

static class RWLSExtension {
struct Disposable : IDisposable {
readonly Action _action;
public Disposable(Action action) {
_action = action;
}
public void Dispose() {
_action();
}
}

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);
}
}

class SlowList<T> {

List<T> baseList = new List<T>();

public void AddRange(IEnumerable<T> items) {
baseList.AddRange(items);
}

public virtual T this[int index] {
get {
Thread.Sleep(1);
return baseList[index];
}
set {
baseList[index] = value;
Thread.Sleep(1);
}
}
}

class SynchronizedList<T> : SlowList<T> {

object sync = new object();

public override T this[int index] {
get {
lock (sync) {
return base[index];
}

}
set {
lock (sync) {
base[index] = value;
}
}
}
}


class ManualReaderWriterLockedList<T> : SlowList<T> {

ReaderWriterLockSlim slimLock = new ReaderWriterLockSlim();

public override T this[int index] {
get {
T item;
try {
slimLock.EnterReadLock();
item = base[index];
} finally {
slimLock.ExitReadLock();
}
return item;

}
set {
try {
slimLock.EnterWriteLock();
base[index] = value;
} finally {
slimLock.ExitWriteLock();
}
}
}
}

class ReaderWriterLockedList<T> : SlowList<T> {

ReaderWriterLockSlim slimLock = new ReaderWriterLockSlim();

public override T this[int index] {
get {
using (slimLock.ReadLock()) {
return base[index];
}
}
set {
using (slimLock.WriteLock()) {
base[index] = value;
}
}
}
}


class Program {


private static void Repeat(int times, int asyncThreads, Action action) {
if (asyncThreads > 0) {

var threads = new List<Thread>();

for (int i = 0; i < asyncThreads; i++) {

int iterations = times / asyncThreads;
if (i == 0) {
iterations += times % asyncThreads;
}

Thread thread = new Thread(new ThreadStart(() => Repeat(iterations, 0, action)));
thread.Start();
threads.Add(thread);
}

foreach (var thread in threads) {
thread.Join();
}

} else {
for (int i = 0; i < times; i++) {
action();
}
}
}

static void TimeAction(string description, Action func) {
var watch = new Stopwatch();
watch.Start();
func();
watch.Stop();
Console.Write(description);
Console.WriteLine(" Time Elapsed {0} ms", watch.ElapsedMilliseconds);
}

static void Main(string[] args) {

int threadCount = 40;
int iterations = 200;
int readToWriteRatio = 60;

var baseList = Enumerable.Range(0, 10000).ToList();

List<SlowList<int>> lists = new List<SlowList<int>>() {
new SynchronizedList<int>() ,
new ReaderWriterLockedList<int>(),
new ManualReaderWriterLockedList<int>()
};

foreach (var list in lists) {
list.AddRange(baseList);
}


foreach (var list in lists) {
TimeAction("Time for " + list.GetType().ToString(), () =>
{
Repeat(iterations, threadCount, () =>
{
list[100] = 99;
for (int i = 0; i < readToWriteRatio; i++) {
int ignore = list[i];
}
});
});
}



Console.WriteLine("DONE");
Console.ReadLine();
}
}
}

关于c# - ReaderWriterLockSlim 扩展方法性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/805412/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com