原来系统中有人写了一个锁住对象的类,他用一个字典,对每一个用户操作,字典中先添加对象的ID为key,value为new object。并希望用Monitor锁住obj来实现同步。
我写了类似的测试代码:
public static class MyLock
{
private static Mutex _mutex = new Mutex();
private static Dictionary<string, object> _dic;
static MyLock()
{
_dic = new Dictionary<string, object>();
}
public static void Lock(string key)
{
if (!string.IsNullOrEmpty(key))
{
if (!_dic.ContainsKey(key))
{
_mutex.WaitOne();
_dic.Add(key, new object());
_mutex.ReleaseMutex();
}
LogManager.WriteLog(key);
Monitor.Enter(_dic[key]);
}
}
public static void Unlock(string key)
{
LogManager.WriteLog(key);
Monitor.Exit(_dic[key]);
}
}
我觉得这是很差的方法。
第一字典本身就不是线程安全的。
第二,用户登录进来有太多的操作,不能保证所有的操作都用Lock()和Unlock包起来。
我参考《CLR via C#(第3版)》中关于锁的介绍,修改了该方法,虽然不算完美,但个人认为会比上面的好点。
我给对象加个私有字典private int _waiter = 0;
当某用户在使用时,把_waiter原子地置为1,这样来阻塞其他用户,该用户离开时把_waiter原子地置为0,阻塞的用户可以使用了。加了个超时1分钟。
class User
{
public string Name { get; set; }
public string Id { get; set; }
private int _waiter = 0;
public void Lock()
{
for (int i = 0; i < 120 && Interlocked.Exchange(ref _waiter, 1) == 1; i++)
{
Thread.Sleep(500);
if (i == 119)
throw new TimeoutException("当前操作已超时。");
}
}
public void Unlock()
{
Thread.VolatileWrite(ref _waiter, 0);
}
}
而且,这个锁不应该是静态的。
很抱歉,我错了,没有必要自己写自旋锁,然后莫名地等待500毫秒。其实锁定一个对象可以通用的用Monitor,对象里定义一个object就可以了。
private object _obj = new object();
public void Lock()
{
Monitor.Enter(_obj);
}
public void Unlock()
{
Monitor.Exit(_obj);
}