zoukankan      html  css  js  c++  java
  • 在多线程编程中lock(string){...}隐藏的机关

     常见误用场景:在订单支付环节中,为了防止用户不小心多次点击支付按钮而导致的订单重复支付问题,我们用 lock(订单号) 来保证对该订单的操作同时只允许一个线程执行。

    这样的想法很好,至少比 lock(处理类的private static object)要好,因为lock订单号想要的效果是只锁当前1个订单的操作,而如果lock静态变量,那就是锁所有的订单,就会导致所有的订单进行排队,这显然是不合理的。

    那么本文开篇说的lock(订单号)的做法可以实现想要的效果吗?我们先用一些代码来还原使用场景。

    如果忽略用户信息及其他验证,那代码差不多是这样:

    1 public ActionResult PayOrder(string orderNumber)
    2 {
    3     lock (orderNumber)
    4     {
    5         //订单支付,消息通知等耗时的操作
    6     }
    7     return View("Success");
    8 }

    这样的代码看起来好像没有什么问题,对于lock关键字,MSDN上面包括能够百度到的资料,好像都是说建议不要使用lock(string),而原因都是同一个。以下这段话摘自MSDN关于lock字符串的建议:

    由于进程中使用同一字符串的任何其他代码将共享同一个锁,所以出现 lock(“myLock”) 问题。

    这句话隐藏了一个巨大的机关,那就是“同一字符串”。

    什么叫“同一字符串”?请看代码:

    static void Main(string[] args)
    {
        var str1 = "abc";
        var str2 = "abc";
    }

    请问上面的str1和str2是同一字符串吗?答案是YES。

    再看:

    static void Main(string[] args)
    {
        var str1 = "abc" + 123;
        var str2 = "abc" + 123;
    }

    上面的str1和str2还是同一字符串吗?答案就是NO了。

    好了,再回到我们订单支付的问题上面来。在我们的代码中, lock(orderNumber) ,当用户手滑一不小心多点了几次,请问每次进入这个action的orderNumber是同一字符串吗?答案是NO。这就是说

    上面处理订单的代码实际上并没有起到任何lock的作用。

    实际上,字符串比较分两种,请看代码:

    static void Main(string[] args)
    {
        var str1 = "abc" + 123;
        var str2 = "abc" + 123;
        Console.WriteLine(str1 == str2);
        Console.WriteLine(object.ReferenceEquals(str1, str2));
    }

    上面的代码第一行输出True,第二行输出False。相信不用我解释你也明白MSDN所说的“同一字符串”了。

    最佳解决方案

    首先,要感谢 Treenew Lyn大神提供的最佳锁定字符串的解决方案,超简单:

    lock(string.Intern(key))
    {
        //...do something
    }

    参考项目:

    请用微信扫一扫,关注天天记账体验

  • 相关阅读:
    Testlink1.9.17使用方法( 第三章 初始配置[配置用户、产品] )
    Testlink1.9.17使用方法(第二章 登录&汉化设置)
    Testlink1.9.17使用方法(第一章 前言)
    TestLink-Windows安装教程
    Linux-Redmine安装方法
    怎么使用Fiddler进行抓包
    配置ADB到Windows环境变量
    Android获取定位权限,获取设备所在的经纬度
    Paint.FontMetrics
    Bitmap上下合成图片
  • 原文地址:https://www.cnblogs.com/leotsai/p/lock-string-trap.html
Copyright © 2011-2022 走看看