• Python多线程编程GIL


    Python GIL:global interpreter lock (cpython)

    1、python中一个线程对应于c语言中的一个线程
    2、gil使得同一个时刻只有一个线程在一个cpu上执行字节码, 无法将多个线程映射到多个cpu上执行

    3、gil会根据执行的字节码行数以及时间片释放gil,gil在遇到io的操作时候主动释放

    查看Python的字节码文件

    import dis
    def fun(a):
        a = a + 1
        return a
    print(dis.dis(fun))

    对于第三点的解释:当我们对同一个变量(total)进行加减操作时,会发现最后的运算结果实际上每次都是不一样的,原因在于gil释放锁的时间有区别

    total = 0
    def add():
        global total
        for i in range(1000000):
            total += 1
    
    
    def desc():
        global total
        for i in range(1000000):
            total -= 1
    
    
    import threading
    
    thread1 = threading.Thread(target=add)
    thread2 = threading.Thread(target=desc)
    thread1.start()
    thread2.start()
    
    thread1.join()
    thread2.join()
    print(total)
    

    Python 多线程编程

    对于io操作来说,多线程和多进程性能差别不大

    Python中使用多线程的方式

    1.通过Thread类实例化

     1 import threading
     2 import time
     3 
     4 
     5 def get_detail_html(url):
     6     print("get detail html started")
     7     time.sleep(3)  # 处理业务
     8     print("get detail html end ")
     9 
    10 
    11 def get_detail_html_url():
    12     print("get detail html url started")
    13     time.sleep(3)  # 处理业务
    14     print("get detail html url end ")
    15 
    16 
    17 thread1 = threading.Thread(target=get_detail_html_url, args=("",))
    18 thread2 = threading.Thread(target=get_detail_html, args=("",))
    19 thread1.start()
    20 thread2.start()
    21 thread2.setDaemon(True)  # 设置守护线程
    22 thread1.join()  # 等待线程执行完成在执行后续主线程
    23 thread2.join()
    View Code
    setDaemon 设置线程为守护线程时:主线程结束,子线程也会结束
    join 设置jion时,表示子线程未运行完成,主线程不会执行jion后的代码
    当二者都不设置时,主线和子线程会并行,且即使主线程运行完成子线程未处理完成会继续运行

    2.继承Thread类完成

     1 class GetDetailHtml(threading.Thread):
     2     def __init__(self, name):
     3         super().__init__(name=name)
     4 
     5     def run(self):
     6         print("get detail html started")
     7         time.sleep(2)
     8         print("get detail html end")
     9 
    10 
    11 class GetDetailUrl(threading.Thread):
    12     def __init__(self, name):
    13         super().__init__(name=name)
    14 
    15     def run(self):
    16         print("get detail url started")
    17         time.sleep(4)
    18         print("get detail url end")
    19 
    20 if  __name__ == "__main__":
    21     thread1 = GetDetailHtml("get_detail_html")
    22     thread2 = GetDetailUrl("get_detail_url")
    23     start_time = time.time()
    24     thread1.start()
    25     thread2.start()
    26 
    27     thread1.join()
    28     thread2.join()
    View Code

    重载run方法,适用与比较复杂的线程

    多线程通信

    1、多线程可以通过共享变量进行通信 (多进程是不行的)

    • 全局变量
      •   不具备线程安全
    • 队列
      •   具备线程安全(Python 双端队列)

     线程同步

    1、使用threading 中Lock锁

    1 lock = Lock()
    2 lock.acquire()
    3  共享变量
    4 lock.release()

    使用锁会影响程序的性能,引发死锁的原因:资源竞争、循环等待

    当一个线程内要多次锁进行资源绑定时,可以使用Python的可从入锁RLock,但是acquire和release次数要相等

    2、使用条件变量(condiction),用于Python复杂线程的通信

      1 import threading
      2 
      3 #条件变量, 用于复杂的线程间同步
      4 # class XiaoAi(threading.Thread):
      5 #     def __init__(self, lock):
      6 #         super().__init__(name="小爱")
      7 #         self.lock = lock
      8 #
      9 #     def run(self):
     10 #         self.lock.acquire()
     11 #         print("{} : 在 ".format(self.name))
     12 #         self.lock.release()
     13 #
     14 #         self.lock.acquire()
     15 #         print("{} : 好啊 ".format(self.name))
     16 #         self.lock.release()
     17 #
     18 # class TianMao(threading.Thread):
     19 #     def __init__(self, lock):
     20 #         super().__init__(name="天猫精灵")
     21 #         self.lock = lock
     22 #
     23 #     def run(self):
     24 #
     25 #         self.lock.acquire()
     26 #         print("{} : 小爱同学 ".format(self.name))
     27 #         self.lock.release()
     28 #
     29 #         self.lock.acquire()
     30 #         print("{} : 我们来对古诗吧 ".format(self.name))
     31 #         self.lock.release()
     32 
     33 #通过condition完成协同读诗
     34 
     35 class XiaoAi(threading.Thread):
     36     def __init__(self, cond):
     37         super().__init__(name="小爱")
     38         self.cond = cond
     39 
     40     def run(self):
     41         with self.cond:
     42             self.cond.wait()
     43             print("{} : 在 ".format(self.name))
     44             self.cond.notify()
     45 
     46             self.cond.wait()
     47             print("{} : 好啊 ".format(self.name))
     48             self.cond.notify()
     49 
     50             self.cond.wait()
     51             print("{} : 君住长江尾 ".format(self.name))
     52             self.cond.notify()
     53 
     54             self.cond.wait()
     55             print("{} : 共饮长江水 ".format(self.name))
     56             self.cond.notify()
     57 
     58             self.cond.wait()
     59             print("{} : 此恨何时已 ".format(self.name))
     60             self.cond.notify()
     61 
     62             self.cond.wait()
     63             print("{} : 定不负相思意 ".format(self.name))
     64             self.cond.notify()
     65 
     66 class TianMao(threading.Thread):
     67     def __init__(self, cond):
     68         super().__init__(name="天猫精灵")
     69         self.cond = cond
     70 
     71     def run(self):
     72         with self.cond:
     73             print("{} : 小爱同学 ".format(self.name))
     74             self.cond.notify()
     75             self.cond.wait()
     76 
     77             print("{} : 我们来对古诗吧 ".format(self.name))
     78             self.cond.notify()
     79             self.cond.wait()
     80 
     81             print("{} : 我住长江头 ".format(self.name))
     82             self.cond.notify()
     83             self.cond.wait()
     84 
     85             print("{} : 日日思君不见君 ".format(self.name))
     86             self.cond.notify()
     87             self.cond.wait()
     88 
     89             print("{} : 此水几时休 ".format(self.name))
     90             self.cond.notify()
     91             self.cond.wait()
     92 
     93             print("{} : 只愿君心似我心 ".format(self.name))
     94             self.cond.notify()
     95             self.cond.wait()
     96 
     97 
     98 
     99 if __name__ == "__main__":
    100     from concurrent import futures
    101     cond = threading.Condition()
    102     xiaoai = XiaoAi(cond)
    103     tianmao = TianMao(cond)
    104 
    105     #启动顺序很重要
    106     #在调用with cond之后才能调用wait或者notify方法
    107     #condition有两层锁, 一把底层锁会在线程调用了wait方法的时候释放, 上面的锁会在每次调用wait的时候分配一把并放入到cond的等待队列中,等到notify方法的唤醒
    108     xiaoai.start()
    109     tianmao.start()
    View Code

     wait和notify要成对出现

    #在调用with cond之后才能调用wait或者notify方法
    #condition有两层锁, 一把底层锁会在线程调用了wait方法的时候释放, 上面的锁会在每次调用wait的时候分配一把并放入到cond的等待队列中,等到notify方法的唤醒

    3、信号量

     1 #Semaphore 是用于控制进入数量的锁
     2 #文件, 读、写, 写一般只是用于一个线程写,读可以允许有多个
     3 
     4 #做爬虫
     5 import threading
     6 import time
     7 
     8 class HtmlSpider(threading.Thread):
     9     def __init__(self, url, sem):
    10         super().__init__()
    11         self.url = url
    12         self.sem = sem
    13 
    14     def run(self):
    15         time.sleep(2)
    16         print("got html text success")
    17         self.sem.release()
    18 
    19 class UrlProducer(threading.Thread):
    20     def __init__(self, sem):
    21         super().__init__()
    22         self.sem = sem
    23 
    24     def run(self):
    25         for i in range(20):
    26             self.sem.acquire()
    27             html_thread = HtmlSpider("https://baidu.com/{}".format(i), self.sem)
    28             html_thread.start()
    29 
    30 if __name__ == "__main__":
    31     sem = threading.Semaphore(3)
    32     url_producer = UrlProducer(sem)
    33     url_producer.start()
    View Code

    4、Semaphore 并发控制

     1 #Semaphore 是用于控制进入数量的锁
     2 #文件, 读、写, 写一般只是用于一个线程写,读可以允许有多个
     3 
     4 #做爬虫
     5 import threading
     6 import time
     7 
     8 class HtmlSpider(threading.Thread):
     9     def __init__(self, url, sem):
    10         super().__init__()
    11         self.url = url
    12         self.sem = sem
    13 
    14     def run(self):
    15         time.sleep(2)
    16         print("got html text success")
    17         self.sem.release()
    18 
    19 class UrlProducer(threading.Thread):
    20     def __init__(self, sem):
    21         super().__init__()
    22         self.sem = sem
    23 
    24     def run(self):
    25         for i in range(20):
    26             self.sem.acquire()
    27             html_thread = HtmlSpider("https://baidu.com/{}".format(i), self.sem)
    28             html_thread.start()
    29 
    30 if __name__ == "__main__":
    31     sem = threading.Semaphore(3)
    32     url_producer = UrlProducer(sem)
    33     url_producer.start()
    View Code
  • 相关阅读:
    新手入门贴之基于 python 语言的接口自动化 demo 小实战
    记新人从 excel 文件中读取字典数据踩的一个坑
    接口自动化 之 unittest+ddt+openpyxl 综合
    接口自动化之unittest+ddt
    接口自动化之unittest初探
    python之类与对象(5)
    python之类与对象(4)
    python之类与对象(3)
    python之类与对象(2)
    传智播客JavaWeb day01 快捷键、XML
  • 原文地址:https://www.cnblogs.com/hoganhome/p/15834593.html
Copyright © 2020-2023  润新知