zoukankan      html  css  js  c++  java
  • 线程的锁

    一、线程锁

    from threading import Thread, Lock
    
    x = 0
    mutex = Lock()
    
    def task():
        global x
        # mutex.acquire()
        for i in range(200000):
            x = x + 1
            # t1 的 x 刚拿到0保存状态就被切了
            # t2 的 x 拿到0 进行+1 1
            # t1 又获取运行了 x = 0 + 1 1
            # 一共加了两次1 真实运算出来的数字本来应该+2,实际只+1
            # 这就产生了数据安全问题
        mutex.release()
        
    if __name__ == '__main__':
        t1 = Thread(target=task)
        t2 = Thread(target=task)
        t3 = Thread(target=task)
        t1.start()
        t2.start()
        t3.start()
        
        t1.join()
        t2.join()
        t3.join()
        print(x)
    

    二、死锁问题

    from threading import Thread, Lock
    import time
    mutex1 = Lock()
    mutex2 = Lock()
    
    class MyThread(Thread):
        def run(self):
            self.task1()
            self.task2()
            
    	def task1(self):
            mutex1.acquire()
            print(f'{self.name} 抢到了 锁1')
            mutex2.acquire()
            print(f'{self.name} 抢到了 锁2')
            mutex2.release()
            print(f'{self.name} 释放了 锁2')
            mutex1.release()
            print(f'{self.name} 释放了 锁1')
            
    	def task2(self):
            mutex2.acquire()
            print(f'{self.name} 抢到了 锁2')
            time.sleep(1)
            mutex1.acquire()
            print(f'{self.name} 抢到了 锁1')
            murex1.release()
            print(f'{self.name} 释放了 锁1')
            mutex2.release()
            print(f'{self.name} 释放了 锁2')
            
    for i in range(3):
        t = MyThread()
        t.start()
        
    # 两个线程
    # 线程1拿到了(锁头2)想要往下执行需要(锁头1)
    # 线程2拿到了(锁头1)想要往下执行需要(锁头2)
    # 互相都拿到了批次想要往下执行的必须条件,互相都不放手里的锁头
    

    三、递归锁(了解)

    递归锁:在同一个线程内可以被多次acquire

    如何释放:内部相当于维护了一个计数器,也就是说同一个线程,acquire了几次就要release几次

    from threading import Thread, Lock, RLock
    
    mutex1 = RLock()
    mutex2 = mutex1
    
    import time
    class MyThread(Thread):
        def run(self):
            self.task1()
            self.task2()
            
    	def task1(self):
            mutex1.acquire()
            print(f'{self.name} 抢到了 锁1 ')
            mutex2.acquire()
            print(f'{self.name} 抢到了 锁2 ')
            mutex2.release()
            print(f'{self.name} 释放了 锁2 ')
            mutex1.release()
            print(f'{self.name} 释放了 锁1 ')
    
        def task2(self):
            mutex2.acquire()
            print(f'{self.name} 抢到了 锁2 ')
            time.sleep(1)
            mutex1.acquire()
            print(f'{self.name} 抢到了 锁1 ')
            mutex1.release()
            print(f'{self.name} 释放了 锁1 ')
            mutex2.release()
            print(f'{self.name} 释放了 锁2 ')
            
    for i in range(3):
    	t = MyThread()
        t.start()
    

    四、信号量

    同进程的一样:Semaphore管理一个内置的计数器,每当调用acquire()时内置计数器-1:

    调用release()时内置计数器+1;计数器不能小于0;当计数器为0时,acquire()将阻塞线程直到其他线程调用release()。

    实例:(同时只有5个线程可以获取semaphore,即可以限制最大连接数5)

    from threading import Thread, semaphore
    import threading
    import time
    
    def task():
        sm.acquire()
        print(f'{threading.current_thread().name} get sm')
        time.sleep(3)
        sm.release()
        
    if __name__ == "__main__":
        sm = Semaphore(5) # 同一时间只有5个进程可以执行.
        for i in range(20):
            t = Thread(target=task)
            t.start()
    

    信号量与进程池是完全不同的概念,进程池Pool(4),最大只能产生4个进程,而且从头到尾都只是这个四个进程,不会产生新的,而信号量是产生一堆线程/进程。

    五、GIL锁

    在Cpython解释器中有一把GIL锁(全局解释器锁),GIL锁本质是一把互斥锁。

    导致了同一个进程下,同一时间只能运行一个线程,无法利用多核优势。

    同一个进程下多个线程只能实现并发不能实现并行。

    为什么要又GIL:

    因为cpython自带的垃圾回收机制不是线程安全的,所以要有GIL锁。

    导致了同一个进程下,同一时间只能运行一个线程,无法利用多核优势。

    分析:

    我们有四个任务需要处理,处理方式肯定是要玩出并发的效果,解决方案可以是:

    方案一:开启四个进程

    方案二:一个进程下,开启四个线程

    计算密集型:推荐使用多进程

    每个都要计算10s

    1.多线程:

    在同一时刻只有一个线程会被执行,也就意味着每个10s都不能省,分开每个都要计算10s,共40.x s

    2.多进程:

    可以并行的执行多个线程,10s+开启进程的时间

    from multiprocessing import Process
    from threading import Thread
    import time
    
    
    # 计算密集型
    def work1():
        res = 0
        for i in range(100000000):  # 1+8个0
            res *= i
    
    
    if __name__ == '__main__':
        t_list = []
        start = time.time()
        for i in range(4):
            # t = Thread(target=work1)
            t = Process(target=work1)
            t_list.append(t)
            t.start()
        for t in t_list:
            t.join()
        end = time.time()
        # print('多线程',end-start) # 多线程 15.413789510726929
        print('多进程', end - start)  # 多进程 4.711405515670776
    
    

    io密集型 推荐多线程

    4个任务每个任务90%的时间都在io。

    每个任务io 10s

    1.多线程:

    可以实现并发,每个线程io的时间不占用cpu, 10s+4个任务的计算时间

    2.多进程

    可以实现并行,10s+1个任务执行的时间+开进程的时间

    from threading import Thread
    from multiprocessing import Process
    import time
    
    # io密集型
    def work1():
        x = 1+1
        time.sleep(5)
    
    
    if __name__ == '__main__':
        t_list = []
        start = time.time()
        for i in range(4):
            t = Thread(target=work1)
            # t = Process(target=work1)
            t_list.append(t)
            t.start()
        for t in t_list:
            t.join()
        end = time.time()
        print('多线程',end-start) #  多线程 5.002625942230225
        # print('多进程',end-start) # 多进程 5.660863399505615
    
    
  • 相关阅读:
    iperf3命令使用
    python 使用多进程无法正常退出
    cfg 4 ocl
    opencv的CMakeLists.txt与makefile写法
    不需要打密码的sudo方法
    Fedora下rstudio-server安装
    Linux下突然不识别无线网卡
    Python使用opencv
    Python version 2.7 required, which was not found in the registry
    MySQL性能优化 — 实践篇1
  • 原文地址:https://www.cnblogs.com/17vv/p/11543905.html
Copyright © 2011-2022 走看看