Roy*_*mir 2 c# multithreading locking .net-4.5
当在线用户向离线用户发送消息时,我会将这些消息保存在ConcurrentDictionary
.每个用户都在自己的Task
(线程)中运行/旋转.
public static ConcurrentDictionary<int, List<MSG>> DIC_PROFILEID__MSGS = new ConcurrentDictionary...
Run Code Online (Sandbox Code Playgroud)
所以方法看起来像:
/*1*/ public static void SaveLaterMessages(MSG msg)
/*2*/ {
/*3*/ var dic = Globals.DIC_PROFILEID__MSGS;
/*4*/
/*5*/
/*6*/ lock (saveLaterMssagesLocker)
/*7*/ {
/*8*/ List<MSG> existingLst;
/*9*/ if (!dic.TryGetValue(msg.To, out existingLst))
/*10*/ {
/*11*/ existingLst = new List<MSG>();
/*12*/ dic.TryAdd(msg.To, existingLst);
/*13*/ }
/*14*/ existingLst.Add(msg);
/*15*/ }
/*16*/ }
Run Code Online (Sandbox Code Playgroud)
请注意lock
at #6
.我这样做是因为如果2
线程在#10
,它们都会导致创建一个新的List
(这是坏的).
但我对lock
"太多" 的事实感到困扰.
换句话说,如果我发送消息offline-user-20
,则没有理由不能发送消息offline-user-22
.
所以我正在考虑创建额外的锁字典:
Dictionary <int , object> DicLocks = new Dictionary <int , object>();
Run Code Online (Sandbox Code Playgroud)
int
关键在哪里userID
稍后,使用初始化每个条目 new Object()
所以现在我的方法看起来像:
public static void SaveLaterMessages(MSG msg)
{
var dic = Globals.DIC_PROFILEID__MSGS;
lock (Globals.DicLocks[msg.To]) //changed here !!!
{
List<MSG> existingLst;
if (!dic.TryGetValue(msg.To, out existingLst))
{
existingLst = new List<MSG>();
dic.TryAdd(msg.To, existingLst);
}
existingLst.Add(msg);
}
}
Run Code Online (Sandbox Code Playgroud)
现在,用户可以在不干扰的情况下将消息插入到不同的离线用户.
题
1)我采用这种方法是对的,还是有更好的方法?
2)我真的很讨厌锁定ConcurrentDictionary
,这是100%不对.我应该定期dictionary
吗?
ConcurrentDictonary 具有帮助您处理所处情况的工具,如果将现有列表的检索/创建切换为单个线程安全操作,则会使问题变得更加简单.
public static void SaveLaterMessages(MSG msg)
{
var dic = Globals.DIC_PROFILEID__MSGS;
List<MSG> existingLst = dic.GetOrAdd(msg.To, (key) => new List<MSG>());
lock(((ICollection)existingLst).SyncRoot)
{
existingLst.Add(msg);
}
}
Run Code Online (Sandbox Code Playgroud)
这会尝试从字典中获取列表并创建一个新列表(如果它不存在),然后它仅锁定添加到列表对象本身列表的非线程安全操作.
如果可能的话,更好的选择是替换你List<MSG>
的线程安全集合ConcurrentQueue<MSG>
,你根本不需要执行任何锁定(执行此操作的能力取决于消息在列表中的使用方式).如果确实需要使用不需要Globals.DicLocks[msg.To]
锁定的List ,则锁定从集合返回的列表对象是完全可以接受的.
您可以从第二个锁定对象获得的一个优点是,如果您要进行大量读取但写入很少,则可以使用a ReaderWriterLockSlim
来允许多个并发读取器但只允许一个写入器.
public static void SaveLaterMessages(MSG msg)
{
var dic = Globals.DIC_PROFILEID__MSGS;
List<MSG> existingLst = dic.GetOrAdd(msg.To, (key) => new List<MSG>());
var lockingObj = GetLockingObject(existingLst);
lockingObj.EnterWriteLock();
try
{
existingLst.Add(msg);
}
finally
{
lockingObj.ExitWriteLock();
}
}
private static ConcurrentDictionary<List<MSG>, ReaderWriterLockSlim> _msgLocks = new ConcurrentDictionary<List<MSG>, ReaderWriterLockSlim>();
public static ReaderWriterLockSlim GetLockingObject(List<MSG> msgList)
{
_msgLocks.GetOrAdd(msgList, (key) => new ReaderWriterLockSlim());
}
//Elsewhere in multiple threads.
public MSG PeekNewestMessage(int myId)
{
var dic = Globals.DIC_PROFILEID__MSGS;
var list = dic[myId];
var lockingObj = GetLockingObject(list);
lockingObj.EnterReadLock();
try
{
return list.FirstOrDefault();
}
finally
{
lockingObj.ExitReadLock();
}
}
Run Code Online (Sandbox Code Playgroud)
但是,我仍然会建议ConcurrentQueue<MSG>
采用这种方法.