我正在运行一些基于.NET线程安全和非线程安全字典的实验,以及我的自定义实验。
每个写入20,000,000(2000万)英镑的结果如下:
这些测试是在单线程环境中进行的,我试图获得非线程安全字典的速度,并确保线程的安全性。
到目前为止结果很有希望,我很惊讶ConcurrentDictionary的处理能力有多差,可能仅适用于某些场景?
无论如何,下面是我用来测试三个字典的代码,你能告诉我我的自定义代码是否是线程安全的吗?我是否必须为 if(_list.ContainsKey(threadId))添加锁定?我不这么认为,因为它只是一个读取,并且当字典中添加了一个元素(写入)时,它被一个锁保护,阻止其他线程试图读取它。
一旦线程拥有字典就没有锁,因为另一个线程无法写入同一个字典,因为每个线程都有自己的字典(基于ManagedThreadId),使其像单个线程一样安全。
主要
using System;
using System.Diagnostics;
namespace LockFreeTests
{
class Program
{
static void Main(string[] args)
{
var sw = Stopwatch.StartNew();
int i = 20000000; // 20 million
IWork work = new Custom(); // Replace with: Control(), Concurrent(), or Custom()
work.Start(i);
sw.Stop();
Console.WriteLine("Total time: {0}\r\nPress anykey to continue...", sw.Elapsed.TotalMilliseconds);
Console.ReadKey(true);
}
}
}
非线程安全
using System.Collections.Generic;
namespace LockFreeTests
{
class Control : IWork
{
public void Start(int i)
{
var list = new Dictionary<int, int>();
for (int n = 0; n < i; n++)
{
list.Add(n, n);
}
}
}
}
线程安全
using System.Collections.Concurrent;
namespace LockFreeTests
{
class Concurrent : IWork
{
public void Start(int i)
{
var list = new ConcurrentDictionary<int, int>();
for (int n = 0; n < i; n++)
{
list.AddOrUpdate(n, n, (a, b) => b);
}
}
}
}
线程安全(尝试添加)
using System.Collections.Concurrent;
namespace LockFreeTests
{
class ConcurrentTryAdd : IWork
{
public void Start(int i)
{
var list = new ConcurrentDictionary<int, int>();
for (int n = 0; n < i; n++)
{
bool result = list.TryAdd(n, n);
if (!result)
{
n--;
}
}
}
}
}
定制
using System.Collections.Generic;
using System.Threading;
namespace LockFreeTests
{
class Custom : IWork
{
private static Dictionary<int, Dictionary<int, int>> _list = null;
static Custom()
{
_list = new Dictionary<int, Dictionary<int, int>>();
}
public void Start(int i)
{
int threadId = Thread.CurrentThread.ManagedThreadId;
Dictionary<int, int> threadList = null;
bool firstTime = false;
lock (_list)
{
if (_list.ContainsKey(threadId))
{
threadList = _list[threadId];
}
else
{
threadList = new Dictionary<int, int>();
firstTime = true;
}
}
for (int n = 0; n < i; n++)
{
threadList.Add(n, n);
}
if (firstTime)
{
lock (_list)
{
_list.Add(threadId, threadList);
}
}
}
}
}
IWORK
namespace LockFreeTests
{
public interface IWork
{
void Start(int i);
}
}
using System;
using System.Diagnostics;
using System.Threading.Tasks;
namespace LockFreeTests
{
class Program
{
static void Main(string[] args)
{
var sw = Stopwatch.StartNew();
int totalWork = 20000000; // 20 million
int cores = Environment.ProcessorCount;
int workPerCore = totalWork / cores;
IWork work = new Custom(); // Replace with: Control(), Concurrent(), ConcurrentTryAdd(), or Custom()
var tasks = new Task[cores];
for (int n = 0; n < cores; n++)
{
tasks[n] = Task.Factory.StartNew(() =>
{
work.Start(workPerCore);
});
}
Task.WaitAll(tasks);
sw.Stop();
Console.WriteLine("Total time: {0}\r\nPress anykey to continue...", sw.Elapsed.TotalMilliseconds);
Console.ReadKey(true);
}
}
}
上面的代码运行528毫秒,速度提高了40%(来自单线程测试)
答案 0 :(得分:0)
这不是线程安全的。
我是否必须为if(_list.ContainsKey(threadId))添加锁定?我不这么认为,因为它只是一个读取,并且当字典中添加了一个元素(写入)时,它被一个锁保护,阻止其他线程试图读取它。
是的,你需要在这里锁定以使其成为线程安全的。
答案 1 :(得分:0)
我刚刚在这里写了我的无锁线程安全写时复制字典实现:
http://www.singulink.com/CodeIndex/post/fastest-thread-safe-lock-free-dictionary
对于快速的写入和查找通常非常快速,通常以100%标准Dictionary
的速度运行而不会锁定。如果您偶尔写作并且经常阅读,那么这是最快的选择。