架构师_程序员

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 118|回复: 1

[资料] .net/c# 用户多线程并发lock(string){...}详解

[复制链接]
跳转到指定楼层
楼主
发表于 2019-7-3 15:25:19
zu
常见误用场景:在订单支付环节中,为了防止用户不小心多次点击支付按钮而导致的订单重复支付问题,我们用 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”) 问题。
这句话隐藏了一个巨大的机关,那就是“同一字符串”。

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


  1. static void Main(string[] args)
  2. {
  3.     var str1 = "abc";
  4.     var str2 = "abc";
  5. }
复制代码
请问上面的str1和str2是同一字符串吗?答案是YES。

再看:


  1. static void Main(string[] args)
  2. {
  3.     var str1 = "abc" + 123;
  4.     var str2 = "abc" + 123;
  5. }
复制代码
上面的str1和str2还是同一字符串吗?答案就是NO了。

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

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

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


  1. static void Main(string[] args)
  2. {
  3.     var str1 = "abc" + 123;
  4.     var str2 = "abc" + 123;
  5.     Console.WriteLine(str1 == str2);
  6.     Console.WriteLine(object.ReferenceEquals(str1, str2));
  7. }
复制代码
上面的代码第一行输出True,第二行输出False。相信不用我解释你也明白MSDN所说的“同一字符串”了。

最佳解决方案

最佳锁定字符串的解决方案:

  1. lock(string.Intern(key))
  2. {
  3.     //...do something
  4. }
复制代码




demo代码:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading;
  6. using System.Threading.Tasks;

  7. namespace itsvse
  8. {
  9.     class Program1
  10.     {
  11.         private static object obj = new object();
  12.         static void Main(string[] args)
  13.         {
  14.             Console.WriteLine("ok");
  15.             for (int i = 0; i < 3; i++)
  16.             {
  17.                 new Thread(() =>
  18.                 {
  19.                     lock (obj)
  20.                     {
  21.                         Console.WriteLine("obj:" + DateTime.Now.ToLongTimeString());
  22.                         Thread.Sleep(5000);
  23.                     }
  24.                 })
  25.                 { IsBackground = true }.Start();
  26.                 new Thread(() =>
  27.                 {
  28.                     lock (string.Intern("itsvse1"))
  29.                     {
  30.                         Console.WriteLine("itsvse1:" + DateTime.Now.ToLongTimeString());
  31.                         Thread.Sleep(5000);
  32.                     }
  33.                 })
  34.                 { IsBackground = true }.Start();
  35.                 new Thread(() =>
  36.                 {
  37.                     lock (string.Intern("itsvse2"))
  38.                     {
  39.                         Console.WriteLine("itsvse2:" + DateTime.Now.ToLongTimeString());
  40.                         Thread.Sleep(5000);
  41.                     }
  42.                 })
  43.                 { IsBackground = true }.Start();
  44.             }
  45.             Console.ReadKey(true);
  46.         }


  47.     }
  48. }
复制代码



在网站中,有时候可能用到全局变量,这个全局变量,在多用户并发访问的时候,可能会出现异常,这时,我们会设置一个全局的锁,但是,这样的弊端是,所有访问都会依次等待。

有些场景,例如:同一个用户15秒内只能评论一次,如果用全局锁的话,在用户激增的时候,评论功能处理会非常慢,非常影响用户体验。

这时,我们可以对每个用户单独设置锁,lock(string){...},锁的名称可以定义为:方法名+用户的id,这样实现了每个用户有独立的锁,在判断评论间隔时间的时候,不会影响到其它用户评论。

(完)




上一篇:MFC的LPSTR类型写法
下一篇:openssl明明已经更新了,apache还是提示使用的旧版?
帖子永久地址: 

架构师_程序员 - 论坛版权1、本主题所有言论和图片纯属会员个人意见,与本论坛立场无关
2、本站所有主题由该帖子作者发表,该帖子作者与架构师_程序员享有帖子相关版权
3、其他单位或个人使用、转载或引用本文时必须同时征得该帖子作者和架构师_程序员的同意
4、帖子作者须承担一切因本文发表而直接或间接导致的民事或刑事法律责任
5、本帖部分内容转载自其它媒体,但并不代表本站赞同其观点和对其真实性负责
6、如本帖侵犯到任何版权问题,请立即告知本站,本站将及时予与删除并致以最深的歉意
7、架构师_程序员管理员和版主有权不事先通知发贴者而删除本文

码农网,只发表在实践过程中,遇到的技术难题,不误导他人。
沙发
发表于 2019-7-3 20:02:32
这是好东西
码农网,只发表在实践过程中,遇到的技术难题,不误导他人。
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

免责声明:
码农网所发布的一切软件、编程资料或者文章仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容。如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。

Mail To:help@itsvse.com

QQ|Archiver|手机版|小黑屋|架构师 ( 鲁ICP备14021824号-2 )|网站地图

GMT+8, 2019-7-18 00:06

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表