zoukankan      html  css  js  c++  java
  • Python学习第46天(同步锁)

      今天首先是继续看了一下关于GIL的问题,其实昨天验证后存在的问题本身之外,还有一个更加令人震惊的问题,为啥listen和game在使用了多线程之后时间缩短了,而之后的add和cheng的用了多线程运算时间却反而增多了,下面我就慢慢叙来:

      首先是我们常处理的任务分为两种类型:

        1.IO密集型(存在大量的数据输入)

        2.计算密集型(非常高端的底层计算,或者像昨天出现数据差异的那种)

      特别说明一下,time.sleep()就是一个类似的等待数据输入过程,这就合理解释了为啥listen和game是节省时间的,大致情况如下图:

       可以发现,当listen运行至等待3秒的时候,多线程就安排了game进行运行,而在game等待5秒的过程中就把listen部分的3秒内容全部执行完了,切换执行的时间是远小于等待io输入的时间,所以其实也就是线程io等待时间那个少,就节约那个的时间。

      然后是add和cheng的问题,这两者基本就没有io操作的等待时间,就是一堆纯计算,但是解释器还要不停的对这两个线程进行切换,所以浪费了很多时间,导致老老实实把一个计算完再计算下一个更为节约时间。

      所以Python语言是更适合进行操作io密集型的任务的,其实生活中的大多数实例就是io密集型,但是还存在一个本质的问题,就是没办法发挥我四核电脑的强大性能,这个该如何实现呢,继续看昨天的图,加了些东东。

       其实这里的GIL是针对每个进程设置了一个锁,那么如果我们多建立一个进程去分别执行两个方法,那不就OK了嘛,这样两个内核同时开始执行任务。

      但是此方法还是推荐高手使用,每增加一次进程内存消耗还是比较大,所以谨慎使用吧,然后教程也没告诉我到底该怎么使用。

      总结:多线程在处理io密集型任务的过程中,就是将cpu等待用户进行数据输入的时间,安排去执行别的线程,等你完成输入后再返回继续执行,从而提高了cpu的使用效率

      然后提到了一个概念:多线程 + 协程

    下面是第二部分:

      上面说清楚了多线程的工作机制,下面就发生了这么一个因为多线程造成的错误:

      要求,利用多线程实现一个递减的过程。

      先看正确形式运行的代码:  

    import time
    import threading
    
    def addNum():
        global num #在每个线程中都获取这个全局变量
        num-=1
    
    num = 100  #设定一个共享变量
    thread_list = []
    for i in range(100):
        t = threading.Thread(target=addNum)
        t.start()
        thread_list.append(t)
    
    for t in thread_list: #等待所有线程执行完毕
        t.join()
    
    print('final num:', num )

       此时最终的输出结果num是0

      然后我们修改一下addNum的形式,然赋值和运算分为两步进行,同时中间休息0.01秒

    import time
    import threading
    
    def addNum():
        global num #在每个线程中都获取这个全局变量
        temp=num
        time.sleep(0.1)
        num =temp-1 #对此公共变量进行-1操作
    
    num = 100  #设定一个共享变量
    thread_list = []
    for i in range(100):
        t = threading.Thread(target=addNum)
        t.start()
        thread_list.append(t)
    
    for t in thread_list: #等待所有线程执行完毕
        t.join()
    
    print('final num:', num )

      然后此时你会发现,num的值会随着time.sleep()的时间长短发生变化

      数字贼小的时候还是有机会等于0的,但是大多数情况还是会变成一个大于0的数字

      大致的运行过程

      原因非常的明显,在第一次递减进入等待的时候,其他线程开始运行,导致可能存在多个temp被赋值为100,这样最终导致了运算结果的偏差,主要是我们一个线程在没有运行完之前进行了线程之间的切换,那么为了保证这样不被切换,我们引入了同步锁

      同步锁:同步锁内程序不运行完,坚决不进行切换

      修改之后就能完美解决上面的问题了

    import time
    import threading
    
    lock = threading.Lock()
    def addNum():
        global num #在每个线程中都获取这个全局变量
        lock.acquire()   #上锁
        temp=num
        time.sleep(0.1)
        num =temp-1 #对此公共变量进行-1操作
        lock.release()   #解锁
    
    num = 100  #设定一个共享变量
    thread_list = []
    for i in range(100):
        t = threading.Thread(target=addNum)
        t.start()
        thread_list.append(t)
    
    for t in thread_list: #等待所有线程执行完毕
        t.join()
    
    print('final num:', num )

      通过增加 lock.acquire() 和 lock.release() 保证了在他们中间运行的代码不会发生线程的切换,这样就能完美保证程序的逐个运行。

      最近理论知识好多啊,然后上班工作好多好杂,脑瓜子很疼,抽空得复习了,还有前面放了很多的卫星,一直没有去做,感觉现在都忘的干净了,晚安啦。。。

  • 相关阅读:
    Button 的CommandName 用法
    如何循序渐进向DotNet架构师发展
    用sqlserver进行分布式查询(链接服务器)(转)
    关于.resx
    OO设计原则总结
    为ASP.NET 2.0网站生成唯一程序集
    依赖倒置、控制反转和依赖注入辨析(转)
    通过http地址获取页面内容
    Sql同表去除重复
    动态启动WCF服务
  • 原文地址:https://www.cnblogs.com/xiaoyaotx/p/12664077.html
Copyright © 2011-2022 走看看