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
    }

    参考项目:

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

  • 相关阅读:
    log4j 悟寰轩
    初学PHP 悟寰轩
    java基本语法注意问题 悟寰轩
    HTTP协议基础 悟寰轩
    SSHweb.xml详解 悟寰轩
    windows和linux查找被入侵后留下的后门文件 悟寰轩
    MFC Link problem
    Manually Walking a Stack
    linux试用(2)安装 mplayer, g++
    子集和问题
  • 原文地址:https://www.cnblogs.com/leotsai/p/lock-string-trap.html
Copyright © 2011-2022 走看看