gpt4 book ai didi

c# - ConcurrentBag的性能,多次读取,罕见的修改

转载 作者:太空宇宙 更新时间:2023-11-03 17:48:26 26 4
gpt4 key购买 nike

我正在尝试建立一个模型,在该模型中,我将多次读取整个集合,并对其进行罕见的添加和修改。

我以为我在阅读文档时可能会在.NET中使用ConcurrentBag,因此对于并发读写来说应该是不错的选择。

代码如下所示:

public class Cache 
{
ConcurrentBag<string> cache = new ConcurrentBag<string>();

// this method gets called frequently
public IEnumerable<string> GetAllEntries()
{
return cache.ToList();
}

// this method gets rarely called
public void Add(string newEntry)
{
// add to concurrentBag
}

public void Remove(string entryToRemove)
{
// remove from concurrent bag
}
}

但是,我已经反编译了 ConcurrentBag类,并在 GetEnumerator上始终采用了锁定,这意味着对GetAllEntries的任何调用都将锁定整个集合,并且将无法执行。

我正在考虑解决此问题,并使用列表以这种方式进行编码。
 public class Cache 
{
private object guard = new object();

IList<string> cache = new List<string>();

// this method gets called frequently
public IEnumerable<string> GetAllEntries()
{
var currentCache = cache;
return currentCache;
}

// this method gets rarely called
public void Add(string newEntry)
{
lock (guard)
{
cache.Add(newEntry);
}
}

public void Remove(string entryToRemove)
{
lock (guard)
{
cache.Remove(entryToRemove);
}
}
}

由于 AddRemove很少被调用,因此我不太在乎锁定对列表的访问。在 Get上,我可能会得到列表的陈旧版本,但是我也不在乎,下次请求就可以了。

第二种实现方式是否可行?

编辑

我已经进行了快速性能测试,结果如下:

设置:用 10000字符串填充内存中的集合。

行动: GetAllEntries同时 50000次。

结果:
00:00:35.2393871使用 ConcurrentBag完成操作(第一个实现) 00:00:00.0036959使用普通列表完成操作(第二种实现)

代码如下:
 class Program
{
static void Main(string[] args)
{
// warmup caches and stopwatch
var cacheWitBag = new CacheWithBag();
var cacheWitList = new CacheWithList();

cacheWitBag.Add("abc");
cacheWitBag.GetAllEntries();

cacheWitList.Add("abc");
cacheWitList.GetAllEntries();


var sw = new Stopwatch();
// warmup stowtach as well
sw.Start();

// initialize caches (rare writes so no real reason to measure here
for (int i =0; i < 50000; i++)
{
cacheWitBag.Add(new Guid().ToString());
cacheWitList.Add(new Guid().ToString());
}
sw.Stop();

// measure
var program = new Program();

sw.Start();
program.Run(cacheWitBag).Wait();
sw.Stop();
Console.WriteLine(sw.Elapsed);

sw.Restart();
program.Run2(cacheWitList).Wait();
sw.Stop();
Console.WriteLine(sw.Elapsed);
}

public async Task Run(CacheWithBag cache1)
{
List<Task> tasks = new List<Task>();
for (int i = 0; i < 10000; i++)
{
tasks.Add(Task.Run(() => cache1.GetAllEntries()));
}

await Task.WhenAll(tasks);
}
public async Task Run2(CacheWithList cache)
{
List<Task> tasks = new List<Task>();
for (int i = 0; i < 10000; i++)
{
tasks.Add(Task.Run(() => cache.GetAllEntries()));
}

await Task.WhenAll(tasks);
}

public class CacheWithBag
{
ConcurrentBag<string> cache = new ConcurrentBag<string>();

// this method gets called frequently
public IEnumerable<string> GetAllEntries()
{
return cache.ToList();
}

// this method gets rarely called
public void Add(string newEntry)
{
cache.Add(newEntry);
}
}


public class CacheWithList
{
private object guard = new object();

IList<string> cache = new List<string>();

// this method gets called frequently
public IEnumerable<string> GetAllEntries()
{
var currentCache = cache;
return currentCache;
}

// this method gets rarely called
public void Add(string newEntry)
{
lock (guard)
{
cache.Add(newEntry);
}
}

public void Remove(string entryToRemove)
{
lock (guard)
{
cache.Remove(entryToRemove);
}
}
}

}

}

最佳答案

要改善InBetween的解决方案,请执行以下操作:

class Cache 
{
ImmutableHashSet<string> cache = ImmutableHashSet.Create<string>();

public IEnumerable<string> GetAllEntries()
{
return cache;
}

public void Add(string newEntry)
{
ImmutableInterlocked.Update(ref cache, (set,item) => set.Add(item), newEntry);
}

public void Remove(string entryToRemove)
{
ImmutableInterlocked.Update(ref cache, (set,item) => set.Remove(item), newEntry);
}
}

这仅执行原子操作(不锁定),并使用.NET不可变类型。

关于c# - ConcurrentBag的性能,多次读取,罕见的修改,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48479433/

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