• 基于线程和进程的深入研究


    cpython中的GIL解释器锁

    基于对GIL解释器锁的问题,我们先来看一下官方文档给出的解释:

     1 """
     2 
     3       In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple
     4 native threads from executing Python bytecodes at once. This lock is necessary mainly
     5 because CPython’s memory management is not thread-safe. (However, since the GIL
     6 exists, other features have grown to depend on the guarantees that it enforces.)
     7 
     8 """
     9 我们能提炼出以下几点:
    10 1.GIL是一个互斥锁:保证数据的安全(以牺牲效率来换取数据的安全)
    11 2.阻止同一个进程内多个线程同时执行(不能并行但是能够实现并发)
    12 3.并发:看起来像同时进行的
    13 4.GIL全局解释器存在的原因是因为Cpython解释器的内存管理不是线程安全的

     

    这里不同于pycharm的垃圾回收机制:1.引用计数 2.标记清除 3.分代回收

    也就是说,在同一进程下的多个线程是不可能实现并行的但是可以实现并发;相反多个进程下的线程是可以实现并行的。

    基于这点,你会问:我曹,python连多线程抖实现不了,是不是太low了啊?我的回答是:不是的。对于多个进程的多线程的并行实现是毋庸置疑的,在同一个进程下的并行分为两种情况:

    1.基于计算密集型

        首先在单核情况下:基于计算的使用多线程较好,因为在相同的进程数下,多线程消耗的资源少。

        在多核的情况下:开四个进程可能需要10s左右,而开四个线程需要40s左右。

    2.基于IO密集型

      首先在单核情况下;多线程比较好一点

      在多核的情况下:多线程比较好一点

    1.基于计算密集型

     1 # 导入相关的模块
     2 from multiprocessing import Process
     3 from theading import Thread
     4 import time, os
     5 
     6 
     7 # 定义计算过程
     8 def work():
     9     res = 0
    10     for i in range(100):
    11         res *= i
    12 
    13 if __name__=='__mian__':
    14     
    15     # 本机的硬件基础
    16     print(os.cpu_count)  # 本机的cpu核心数为4
    17     lis = []
    18     start = time.time()
    19     for i in range(8):
    20         p = Process(target = work) # 创建的线程这里不做演示,类似的
    21         lis.append(p)
    22        p.start()
    23     
    24     for i in lis:
    25         i.join()
    26     end = time.time()
    27     # 打印输出使用的时间
    28     print('use time is :%s' %(end - start))

    2.基于IO的型

    from multiprocessing import Process
    from threading import Thread
    import os, time
    
    
    # def work():
    #     res = 0
    #     for i in range(100):
    #         res += i
    
    
    # 基于IO型
    def sleep():
        time.sleep(3)
    
    if __name__ == '__main__':
        lis = []
        start = time.time()
        for i in range(8):
            p = Process(target=sleep)
            # p = Thread(target=sleep)
            lis.append(p)
            p.start()
        for j in lis:
            j.join()
        end = time.time()
        print('use time is : %s' % (end - start))

    GIL解释器锁和互斥锁的对比

    大方面:GIL解释器锁针对的是解释器;互斥锁针对的是操作的数据。

     

     1 from threading import Thread,Lock
     2 import time
     3 
     4 mutex = Lock()
     5 
     6 n = 100
     7 
     8 def task():
     9     global n
    10     mutex.acquire()
    11     tmp = n
    12     time.sleep(0.1)
    13     n = tmp - 1
    14     mutex.release()
    15 
    16 t_list = []
    17 for i in range(100):
    18     t = Thread(target=task)
    19     t.start()
    20     t_list.append(t)
    21 
    22 for t in t_list:
    23     t.join()
    24 
    25 print(n)
    26 
    27 """
    28 对于不同的数据,要想保证安全,需要加不同的锁处理
    29 GIL并不能保证数据的安全,它是对Cpython解释器加锁,针对的是线程
    30 保证的是同一个进程下多个线程之间的安全
    31 """

     

    死锁和递归锁

     1 from threading import Thread,Lock,RLock
     2 import time
     3 
     4 """
     5 自定义锁一次acquire必须对应一次release,不能连续acquire
     6 递归锁可以连续的acquire,每acquire一次计数加一:针对的是第一个抢到我的人
     7 """
     8 import random
     9 #
    10 # mutexA = Lock()
    11 # mutexB = Lock()
    12 mutexA = mutexB = RLock()  # 抢锁之后会有一个计数 抢一次计数加一 针对的是第一个抢到我的人
    13 
    14 
    15 class MyThead(Thread):
    16     def run(self):
    17         self.func1()
    18         self.func2()
    19 
    20     def func1(self):
    21         mutexA.acquire()
    22         print('%s 抢到A锁了'%self.name)
    23         mutexB.acquire()
    24         print('%s 抢到B锁了' % self.name)
    25         mutexB.release()
    26         print('%s 释放了B锁'%self.name)
    27         mutexA.release()
    28         print('%s 释放了A锁'%self.name)
    29 
    30     def func2(self):
    31         mutexB.acquire()
    32         print('%s 抢到了B锁'%self.name)
    33         time.sleep(1)
    34         mutexA.acquire()
    35         print('%s 抢到A锁了' % self.name)
    36         mutexA.release()
    37         print('%s 释放了A锁' % self.name)
    38         mutexB.release()
    39         print('%s 释放了B锁' % self.name)
    40 
    41 
    42 for i in range(100):
    43     t = MyThead()
    44     t.start()

     

    线程Queue

    import queue
    
    # 1.普通q
    # 2.先进后出q
    # 3.优先级q
    
    
    # queue包含的有Queue,LifoQueue,PriorityQueue
    
    # q=queue.Queue(3)
    # q.put(1)
    # q.put(2)
    # q.put(3)
    # print(q.get())
    # print(q.get())
    # print(q.get())
    
    # q = queue.LifoQueue(5)
    # q.put(1)
    # q.put(2)
    # q.put(3)
    # q.put(4)
    # print(q.get())
    
    
    
    # 优先级q
    # q = queue.PriorityQueue()
    # q.put((10,'a'))
    # q.put((-1,'b'))
    # q.put((100,'c'))
    # print(q.get())
    # print(q.get())
    # print(q.get())

    信号量

     

     1 from threading import Thread,Semaphore
     2 import time
     3 import random
     4 sm = Semaphore(5)  # 五个厕所五把锁
     5 # 跟你普通的互斥锁区别在于,普通的互斥锁是独立卫生间,所有人抢一把锁
     6 # 信号量 公共卫生间 有多个坑,所有人抢多把锁
     7 
     8 
     9 
    10 def task(name):
    11     sm.acquire()
    12     print('%s正在蹲坑'%name)
    13     # 模拟蹲坑耗时
    14     time.sleep(random.randint(1,5))
    15     sm.release()
    16 
    17 
    18 if __name__ == '__main__':
    19     for i in range(20):
    20         t = Thread(target=task,args=('伞兵%s号'%i,))
    21         t.start()

     

     

  • 相关阅读:
    分享一个详情页
    ES6初探,变量的声明
    ES6初探,什么是ES6
    提问回顾
    个人阅读&个人总结
    结对项目-数独程序扩展
    个人作业Week3-案例分析
    个人作业Week2-代码复审
    个人作业1
    【个人项目】数独
  • 原文地址:https://www.cnblogs.com/wanglei957/p/10832186.html
Copyright © 2020-2023  润新知