agtal吧 关注:108贴子:19,825

来杂谈一下多线程里的「锁」

只看楼主收藏回复

https://b23.tv/BV1UJ411s7rw
1L空


IP属地:加拿大来自手机贴吧1楼2022-04-16 12:38回复
    锁这种东西初学的时候大家也许都有一个误区。受辣鸡语言Java的的影响,大家都有一种锁是用来锁住变量的错觉。其实不是的。锁这种东西锁住的是代码块,也就是一行行的计算机指令。


    IP属地:加拿大来自手机贴吧2楼2022-04-16 12:41
    回复
      ```
      lock(m);
      f();
      unlock (m);
      ```
      就以这一段代码为例吧。假设m为锁的名字,f为锁住区域所执行的代码。对于这样一段代码,其时间复杂度我觉得可以看作一个概率问题。
      假设同时执行这一段代码的线程数有N个,并且将每个线程用i来标记,将每个线程被执行的概率用p_i来标记,将每个线程执行f的时间用f_i来标记,再将每个线程耗在等待上浪费的时间用t_i来标记...
      所有N个线程所浪费的时间,大致能够标记为 (f_i / p_i) + ( t_i / p_i ) ; i = 1 ... N
      用人话说,大概就是每个线程在疯狂被打断的情况下执行f所需要的时间,再加上每个线程花在等待上的时间。可以看出,除了工程师能够控制的f以外,咱们还能够通过算法减少t_i或者增加跟f_i粘着的那个p_i来优化这一段多线程代码。
      (我好像说了一大段废话,不过既然这是杂谈那也无所谓啦)


      IP属地:加拿大来自手机贴吧3楼2022-04-16 13:25
      回复
        先从简单的p_i说起吧。
        微软操作系统里曾经有一种很邪门的锁(现在还有没有我是真的不知道了),名叫调度优先级提升锁,在kmdf里叫wdfspinlock。
        这种锁在调用lock的时候会把当前线程的执行优先度升至最高,并且暂停CPU的打算机制,从而使得在机器上(同一CPU)运行的其他线程无法执行,达到将p_i变成1的效果。
        对于单核系统来说(例如系统驱动之类的东西也可以被当作是单核系统),这种锁已经挺完美的了,只是如果用户为PC且用户的CPU比较慢或者工程师比较菜写出来的f要花很长时间来执行的话,在用户眼里它的机器就会显得很“卡”。毕竟这种锁的调度优先级是有可能会比输入设备、画面刷新等系统的优先级还要高的;CPU时间都给f了,那当然卡了。
        对于服务器来说,这种锁必须得实现得很小心----特别是对于网络层来说,得格外注意。如果这种锁把网络层的IO给打断了的话,最直接的后果就是丢包或者连接中断。丢包或者连接中断对业务有没有影响这得取决于业务的实际内容,但是原本的机器明明有办法不丢包的,却败在了一个调度优先级上,十分的不经济。
        对于高负载运算来说的话这种调度优先级的锁倒是无伤大雅。本来负载运算就是被打断的次数越少越好的,p_i都是1了,还会有什么不好的吗。


        IP属地:加拿大来自手机贴吧4楼2022-04-16 13:49
        回复
          对于多核系统的p_i来说,一般有两种处理方式。一种方式是给每个CPU弄一段专属的内存跟一个专属的锁m,以达到每个核之间都相互独立。至于优先级嘛,每一个核都能够有一个属于自己、跟其他核无关的优先队列,无需担心核心之间打架的问题。
          这里举个例子吧。假设f需要对某一个变量c进行赋值操作,再假设系统有8核的处理器。那么与其只设一个c,咱们可以创建一整个类似数组的东西,c_1到c_8。在f中对c操作之前,可以先把CPU的id给先算出来,然后根据id找到对应的c_id再进行赋值操作。在需要聚合变量的时候,再另作考虑(如果对一致性要求极高的话可以直接抱死整台机器再聚合,但如果对一致性不那么看重的话可以在每个处理器都跑一遍聚合变量的代码)。
          这种内存相互独立的解决方案其实还蛮聪明的,甚至在某些应用下还可以把锁去掉,把内存换成那种原子型的。不过这种方案有一定的局限性----其一,(对于写代码的人来说)不方便...其二,某些应用情景不适合在内存互相独立的环境下运行。


          IP属地:加拿大来自手机贴吧5楼2022-04-16 14:18
          回复
            还有一种方式便是使用一些比较复杂的数据结构来实现多核心的优先级同步、再考虑运行f了。
            我可以稍微脑补一种实现方式?
            但是我想表达的是无论哪种实现方式都会对别的核心的t_i或者p_i产生影响,从而使得系统总体的吞吐量下降。


            IP属地:加拿大来自手机贴吧6楼2022-04-16 14:23
            回复
              嗨 停机了停机了
              无极突然想起来自己的贴吧密码了于是上来水了一贴。下次更新是什么时间就不知道咯


              IP属地:加拿大来自手机贴吧7楼2022-04-16 14:24
              回复
                想起来密码可还行


                IP属地:安徽来自iPhone客户端8楼2022-05-01 09:37
                回复
                  我还以为有故事听了(


                  IP属地:江苏9楼2022-05-02 19:56
                  回复
                    想起密码好啊


                    IP属地:山东来自iPhone客户端10楼2022-07-08 16:08
                    回复
                      锁这种东西初学的时候大家也许都有一个误区。受辣鸡语言Java的的影响,大家都有一种锁是用来锁住变量的错觉。
                      笑死我了


                      IP属地:北京11楼2022-07-14 10:42
                      回复
                        嘛,解决多线程效率问题主要还是看这个方面:
                        一个方面是系统的瓶颈出在哪,并且瓶颈能否规避;
                        如果不能规避的话,则可以看看系统中多线程库有无可优化的地方(例如服务器框架搭配上程序员写的代码会不会产生内存内存泄漏,例如系统中有没有很严重的资源抢夺,等等);
                        如果这些方面都还是无法解决效率问题的话,则需要从硬件方面下手了(例如根据服务运行时的具体系统指标,来判断服务器需要什么样的CPU 内存 网卡 甚至是显卡跟ASIC等等)


                        IP属地:加拿大来自手机贴吧12楼2022-08-22 13:54
                        回复
                          无法避免的锁解决不了的事情 有些还真能通过升级硬件或者升级网络来解决的
                          举一个最简单的例子,假设每个用户都有一个比较长的用户id, 假设业务需要服务器集群访问以用户id前x位数字来命名的数据库堆文件(假设一共有N个堆文件),且访问文件需要用到无法避免的写入锁。
                          这个系统提升效率的办法有很多种,但其中一种可以说是最邪门的,便是准备N台机器,每台机器只打开一个堆文件,然后当用户访问服务器集群的时候,集群根据用户id,映射出一台其所对应的那一,处理堆文件的机器。这下好了,每一台机器都不用锁堆文件了,岂不美哉。


                          IP属地:加拿大来自手机贴吧13楼2022-08-22 14:05
                          回复
                            wuji教我


                            IP属地:江苏来自手机贴吧14楼2022-11-13 09:12
                            收起回复
                              不杂谈了,结论是最好别用锁


                              IP属地:加拿大15楼2023-02-14 15:26
                              回复