我试图拥有keys
的集合......如果密钥不存在,那么我需要DoSomeMethod()
然后将密钥添加到集合中。
问题是,这需要能够同时处理多个尝试添加相同密钥的线程。
如果两个线程具有相同的密钥,则只有一个线程将执行DoSomeMethod()
而另一个线程需要等待。
我已经考虑过使用ConcurrentDictionary
和GetOrAdd
(with the Func(..)
param option)方法了,但似乎两者都是“关闭”。如果两个线程具有相同的密钥,则同时进行。我认为GetOrAdd
的实现将是
value
获取key
。 value
则执行任何操作..现在设置值。 value
......以及任何其他key
点击将等到锁完成。 感觉就像GetOrAdd
方法所调用的自定义方法一样,线程安全。
MSDN文档也提出了这个建议吗?
备注
如果您在不同的线程上同时调用GetOrAdd
,可能会多次调用addValueFactory
,但每次调用时其键/值对可能不会添加到字典中
Contrite示例:我将文件从source
复制到destination
。
source
文件时,如果我们尝试检查并创建目标文件夹,请检查该集合。key
,那么检查目标文件夹是否不存在......如果它不存在则创建它。 因此,如果我们还没有完成目标文件夹,我们只会创建目标文件夹。
我想要锁定集合KEY ....
答案 0 :(得分:3)
<table>
<thead>
<tr>
<th>Col 1</th>
<th>Col 2</th>
<th>Col 3</th>
<th>Col 4</th>
</tr>
</thead>
<tbody>
<tr>
<td>Row 1</td>
<td>Row 1</td>
<td>Row 1</td>
<td>Row 1</td>
</tr>
....
</tbody>
</table>
我想我应该稍微描述一下。基本上这里发生的是,如果两个调用者同时在public class OnceOnlyConcurrent<TKey, TValue>
{
private readonly ConcurrentDictionary<TKey, Lazy<TValue>> _dictionary = new ConcurrentDictionary<TKey, Lazy<TValue>>();
public TValue GetOrAdd(TKey key, Func<TValue> computation)
{
var result = _dictionary.AddOrUpdate(key, _ => new Lazy<TValue>(computation, LazyThreadSafetyMode.ExecutionAndPublication), (_, v) => v);
return result.Value;
}
}
上发生,AddOrUpdate
将始终调用addValueFactory
代理两次,这两个调用除了返回之外都不会执行任何操作包含计算的AddOrUpdate
引用。
在Lazy<T>
内,两个结果都会被捕获,但会删除一个。只有AddOrUpdate
的一个实例将返回给Lazy<T>
的两个调用者,因此一个AddOrUpdate
将控制被调用的计算。
然后,在下一行,当我们要求Lazy<T>
时,这实际上将触发此自定义.Value
的一个调用者的计算,而另一个将在第一个计算时阻塞 - 这是GetOrAdd
(Lazy<T>
)的第二个参数的功能。顺便说一下,这是LazyThreadSafteMode.ExecutionAndPublication
的默认行为,所以你真的不需要第二个参数 - 我在这篇文章中用它来更清楚。
当然,这段代码也可以作为扩展方法编写,但不幸的是,您必须知道在里面创建一个包含Lazy<T>
个对象的字典,所以我认为它更适合作为{Lazy<T>
的包装类。 {1}}。
答案 1 :(得分:-1)
不需要GetOrAdd
。对路径和密钥存在的简单检查就足够了:
class FileWorker
{
private object _sync;
private IDictionary<string, Task> _destTasks;
public FileWorker()
{
_sync = new object();
_destTasks = new Dictionary<string, Task>();
}
public async Task Copy(IEnumerable<FileInfo> files, string destinationFolder)
{
await Task.WhenAll(files.Select(f => Copy(f, destinationFolder)));
}
private async Task CreateDestination(string path)
{
await Task.Run(() =>
{
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
});
}
private Task Destination(string path)
{
lock(_sync)
{
if (!_destTasks.ContainsKey(path))
{
_destTasks[path] = CreateDestination(path);
}
}
return _destTasks[path];
}
private async Task Copy(FileInfo file, string destinationFolder)
{
await Destination(destinationFolder).ContinueWith(task => file.CopyTo(Path.Combine(destinationFolder, file.Name), true));
}
}
class Program
{
static void Main(string[] args)
{
var file1 = new FileInfo("file1.tmp");
using(var writer = file1.CreateText())
{
writer.WriteLine("file 1");
}
var file2 = new FileInfo("file2.tmp");
using(var writer = file2.CreateText())
{
writer.WriteLine("file 2");
}
var worker = new FileWorker();
worker.Copy(new[] { file1, file2 }, @"C:\temp").Wait();
Console.ReadLine();
}
}