请教下关于锁的用法

wapit 2016-06-01 01:25:43
代码如下:
public ActionResult Index(string uid)
{
var user=GetUser(uid);
if(user==null){
user=CreateUser(uid);//假定耗时1s
}
...
}

以上代码中,由于CreateUser耗时较长,导致高并发情况下CreateUser重复执行,带来问题,于是,做了以下改进:

static object objLock=new object();
public ActionResult Index(string uid)
{
lock(objLock){
var user=GetUser(uid);
if(user==null){
user=CreateUser(uid);//假定耗时1s
}
...
}
}

可这时,带来了另一个问题,大量用户被阻挡在lock外面。

请教:有什么办法,能实现lock的功能,但是允许不同的uid可以同时执行?
...全文
251 8 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
8 条回复
切换为时间正序
请发表友善的回复…
发表回复
Forty2 2016-06-01
  • 打赏
  • 举报
回复
如果是优化是避免本地重复操作,那么:

        public static void Index(string uid)
        {
            using (UserLocks.LockDown(uid))
            {
                var user = GetUser(uid);
            }
        }

class UserLocks : IDisposable
{
    static Dictionary<string, object> perUserLocks = new Dictionary<string, object>();
    private string userName;
    private UserLocks(string userName)
    {
        this.userName = userName;
    }

    public static IDisposable LockDown(string userName)
    {
        object lockObj;
        lock (perUserLocks)
        {
            if (!perUserLocks.TryGetValue(userName, out lockObj))
            {
                perUserLocks[userName] = lockObj = new object();
            }
        }
        Monitor.Enter(lockObj);
        return new UserLocks(userName);
    }

    void IDisposable.Dispose()
    {
        object lockObj;
        lock (perUserLocks)
        {
            if (perUserLocks.TryGetValue(userName, out lockObj))
            {
                Monitor.Exit(lockObj);
            }
        }
    }
}
wapit 2016-06-01
  • 打赏
  • 举报
回复
引用 4 楼 sp1234 的回复:
...Dictionary<stirng, object> LockFlags...
懂了……谢谢!
引用 4 楼 sp1234 的回复:
...要想避免重复查询数据库,那么就应该把查询结果放到 Cache 实例中...
继续请教下,如我用得较多的放缓存的代码:

   public User GetLastUser()
        {
            var key = "Biz_Users_GetLastUser";
            User model = null;
            if (HttpRuntime.Cache[key] != null)
                model = HttpRuntime.Cache[key] as User ;
            if (model != null)
                return model;
            model = GetLastUserFromDb();   //假定耗时100ms
            if (model == null)
                return null;
            HttpRuntime.Cache.Insert(key, model, null, DateTime.Now.AddSeconds(10), Cache.NoSlidingExpiration);
            return model;
        }
可并发大时,仍然会导致在这100ms里有很多请求进入方法,因为此时缓存并没值,所以全涌入GetFromDb语句了,一些GetFromDb耗时较长的方法上(如获取排名列表等),我又不得不使用Lock了……这种情况,有啥好办法?
  • 打赏
  • 举报
回复
比如说,如果你的软件总是在一启动时就集中去取固定某两位用户的 UserInfo,你可以单独设置两个 lockflag,例如
private static object Lock1234 = new object();
private static object Lock888 = new object();
而对获取其它用户的资料,你完全不应该使用 lock。 这里的逻辑不应该考虑 lock。
Forty2 2016-06-01
  • 打赏
  • 举报
回复
CreateUser如果是数据库出现重复等问题,应该在数据库层面解决。因为: 1、”高并发“的最直接的解决办法,就是增加服务器。 2、任何在本地加锁,解决CreateUser重复的方法,对多个服务器没有效果。 如果,你只关心‘本地’的重复执行问题。那么,你可以减低加锁的范围,可以不同的uid对应一个锁。 数据结构上可以用Dictionary<string, object>,key就是uid,object就是锁。
  • 打赏
  • 举报
回复
lock 针对的就是单个的对象,所以你懂的,要想针对单个 uid 进行加锁,那么你首先设计能够保存针对单个uid所创建的数据结构就行了。 例如 Dictionary<stirng, object> LockFlags。 或者你先使用 GetUserLockFlag(uid) 获取加锁对象,然后再使用 lock 来加锁。此时这个 GetUserLockFlag 由于是纯粹内存操作,自然不会用1s这么长时间。 回到你的问题本身,实际上“高并发情况下CreateUser重复执行”这并不是应该使用 lock 的理由!要想避免重复查询数据库,那么就应该把查询结果放到 Cache 实例中,例如可能写
UserInfo GetUserInfo(string id)
{
  var key = "用户_" + id + "_的信息";
  var cache = HttpRuntime.Cache;
  var res = (UserInfo)cache[key];
  if (res == null)
  {
    res = 查询数据库(id);
    cache.Insert(key, res);
  }
  return res;
}
在 Insert 操作中,你还可以提供更多的输入参数,为缓存单元设置“时间窗口、依赖于某文件改变、依赖于数据库表的变动、依赖于其它缓存单元改变”等等 CacheDependency。 缓存技术的关键不在于会滥用内存,而在于会设置 CacheDependency 从而避免滥用内存来保存脏数据。 回到你的问题本身。当有高并发访问相同的 id 的用户资料时,其实你无法避免一开始就有2、3个任务去同时访问后台数据服务,但是你可以通过缓存技术来避免随后的请求再去访问后台。
wapit 2016-06-01
  • 打赏
  • 举报
回复
引用 1 楼 hanjun0612 的回复:
不推荐使用lock。 前台ajax访问时,点击按钮提交后,通过ajax的beforesend,显示一个等待的图标。当提交完成后,ajax的complete取消这个图标,来防止用户重复点击提交按钮。
前端控制是不保险的……
引用 2 楼 hanjun0612 的回复:
个人感觉不应该使用lock。 不然某人如果恶意刷新的话,大量用户还是会被锁住
所有求更好的办法……
正怒月神 2016-06-01
  • 打赏
  • 举报
回复
个人感觉不应该使用lock。 不然某人如果恶意刷新的话,大量用户还是会被锁住
正怒月神 2016-06-01
  • 打赏
  • 举报
回复
不推荐使用lock。 前台ajax访问时,点击按钮提交后,通过ajax的beforesend,显示一个等待的图标。当提交完成后,ajax的complete取消这个图标,来防止用户重复点击提交按钮。

111,094

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • AIGC Browser
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧