Python并发编程04 /多线程、生产消费者模型、线程进程对比、线程的方法、线程join、守护线程、线程互斥锁

Python并发编程04 /多线程、生产消费者模型、线程进程对比、线程的方法、线程join、守护线程、线程互斥锁

1. 生产消费者模型

  • 定义:编程思想,模型,设计模式,理论等等,都是一种编程的方法,遇到类似的情况,套用即可.

  • 生产者消费者模型三要素:

    生产者: 产生数据的

    消费者: 接收数据做进一步处理的

    容器: 队列,起到缓冲的作用,平衡生产力与消费力,解耦.

  • 代码示例:

    from multiprocessing import Process
    from multiprocessing import Queue
    import time
    import random
    
    def producer(q,name):
        for i in range(1,6):
            time.sleep(random.randint(1,2))
            res = f'{i}号包子'
            q.put(res)
            print(f'生产者{name} 生产了{res}')
    
    def consumer(q,name):
        while 1:
            try:
                food = q.get(timeout=3)
                time.sleep(random.randint(1, 3))
                print(f'33[31;0m消费者{name} 吃了{food}33[0m')
            except Exception:
                return
    
    if __name__ == '__main__':
        q = Queue()
        p1 = Process(target=producer,args=(q,'张三'))
        p2 = Process(target=consumer,args=(q,'李四'))
        p1.start()
        p2.start()
    

2. 线程的理论知识

  • 什么是线程

    标准描述开启一个进程:开启一个进程:进程会在内存中开辟一个进程空间,将主进程的资料数据全部复制一份,线程会执行里面的代码.

    ***进程是资源单位, 线程是执行单位;是操作系统调度的最小单元,是进程中的实际运作单位.

  • 线程vs进程

    1. 开启进程的开销非常大,比开启线程的开销大很多.
    2. 开启线程的速度非常快.要快几十倍到上百倍.
    3. 同一进程内线程与线程之间可以共享数据,进程与进程之间需借助队列等方法实现通信.
  • 线程的应用

    单个进程开启三个线程.并发的执行任务.

    并发:一个cpu 看起来像是同时执行多个任务.

  • 主线程子线程没有地位之分

    一个主线程在干活,当干完活了,得等待其他线程干完活之后,才能结束本进程.

3. 开启线程的两种方式

  • 方式一

    from threading import Thread
    import time
    
    def task(name):
        print(f'{name} is running')
        time.sleep(1)
        print(f'{name} is gone')
    
    if __name__ == '__main__':
    
        t1 = Thread(target=task,args=('张三',))  # args的参数一定要是元组
        t1.start()
        print('===主线程')  # 线程是没有主次之分的.
    
  • 方式二

    from threading import Thread
    import time
    
    class MyThread(Thread):
        def __init__(self,name,l1,s1):
            super().__init__()
            self.name = name
            self.l1 = l1
            self.s1 = s1
        def run(self):
            print(f'{self.name} is running')
            time.sleep(1)
            print(f'{self.name} is gone')
    
    if __name__ == '__main__':
        t1 = MyThread('张三', [1,2,3], '180')
        t1.start()
        print('=====主线程')
    

4. 线程、进程对比代码验证

  • 开启速度对比

    # 多进程
    from threading import Thread
    from multiprocessing import Process
    import os
    
    def work():
        print('hello')
    
    if __name__ == '__main__':
        t=Process(target=work)
        t.start()
        print('主线程/主进程')
        
    # 多线程
    from threading import Thread
    import time
    
    def task(name):
        print(f'{name} is running')
        time.sleep(1)
        print(f'{name} is gone')
    
    if __name__ == '__main__':
        t1 = Thread(target=task,args=('张三',))
        t1.start()
        print('===主线程')
        
    # 结论:
    # 多进程是先打印的'主线程/主进程',多线程是先打印的'张三 is running',所以得出线程的开启速度比进程快
    
  • 对比pid

    # 多进程
    from multiprocessing import Process
    import time
    import os
    
    def task(name):
        print(f'子进程: {os.getpid()}')
        print(f'主进程: {os.getppid()}')
        
    if __name__ == '__main__':
        p1 = Process(target=task,args=('张三',))  # 创建一个进程对象
        p2 = Process(target=task,args=('李四',))  # 创建一个进程对象
        p1.start()
        p2.start()
        print(f'==主{os.getpid()}')
    
    # 多线程
    from threading import Thread
    import os
    
    def task():
        print(os.getpid())
        
    if __name__ == '__main__':
        t1 = Thread(target=task)
        t2 = Thread(target=task)
        t1.start()
        t2.start()
        print(f'===主线程{os.getpid()}')
    
    # 结论:
    # 线程没有pid,进程才有pid
    
  • 共享数据的对比,同一个进程内线程共享内部数据

    from threading import Thread
    import os
    
    x = 3
    def task():
        global x
        x = 100
    
    if __name__ == '__main__':
        t1 = Thread(target=task)
        t1.start()
        t1.join()
        print(f'===主线程{x}')
        
    # 同一进程内的资源数据对于这个进程内的多个线程来说是共享的.
    

5. 线程的其他方法

  • 代码示例:

    from threading import Thread
    from threading import currentThread
    from threading import enumerate
    from threading import activeCount
    import os
    import time
    
    def task():
        print(currentThread())  # 获取当前线程对象
        time.sleep(1)
        print('666')
        
    if __name__ == '__main__':
        t1 = Thread(target=task,name='线程1')  # name 设置线程名
        t2 = Thread(target=task,name='线程2')
        t1.start()
        t2.start()
        time.sleep(2)
        print(t1.isAlive())  # 判断线程是否活着
        print(t1.getName())  # 获取线程名
        t1.setName('子线程-1')  # 设置线程的名称
        print(t1.name)   # 获取线程名  ***
    
        # threading方法
        print(currentThread())  # 获取当前线程的对象
        print(enumerate())  # 返回一个列表,包含所有的线程对象
        print(activeCount())  # 获取当前线程存活个数 ***
        print(f'===主线程{os.getpid()}')
    

6. 线程join

  • join: 阻塞 告知主线程要等待我子线程执行完毕之后再执行主线程

  • 代码示例:

    from threading import Thread
    import time
    
    def task(name):
        print(f'{name} is running')
        time.sleep(1)
        print(f'{name} is gone')
    
    if __name__ == '__main__':
        start_time = time.time()
        t1 = Thread(target=task,args=('张三',))
        t2 = Thread(target=task,args=('李四',))
        t3 = Thread(target=task,args=('王五',))
    
        t1.start()
        t1.join()
        t2.start()
        t2.join()
        t3.start()
        t3.join()
    
        print(f'===主线程{time.time() - start_time}')  # 线程是没有主次之分的.
    

7. 守护线程

  • 守护线程:等待非守护子线程以及主线程结束之后,结束.

  • 代码示例:

    示例一:简单使用守护线程

    from threading import Thread
    import time
    
    def sayhi(name):
        print('你好!')
        time.sleep(2)
        print('%s say hello' %name)
    
    if __name__ == '__main__':
        t = Thread(target=sayhi,args=('张三',))
        # 方式1:
        t.setDaemon(True)  # 必须在t.start()之前设置
        # 方式2:
        t.daemon = True
        t.start()
        print('主线程')
    
    

    示例二:守护线程在非守护线程结束前先结束

    from threading import Thread
    import time
    
    def foo():
        print(123)   # 第1步
        time.sleep(1)
        print("end123")  # 第4步
    
    def bar():
        print(456)  # 第2步
        time.sleep(3)
        print("end456")  # 第5步
    
    t1=Thread(target=foo)
    t2=Thread(target=bar)
    
    t1.daemon=True
    t1.start()
    t2.start()
    print("---main---")  # 第3步
    
    # 结果:
    # 123
    # 456
    # ---main---
    # end123
    # end456
    
    

    示例三:守护线程在非守护线程结束之后结束

    from threading import Thread
    import time
    
    def foo():
        print(123)  # 第1步
        time.sleep(3)
        print("end123")
    
    def bar():
        print(456)  # 第2步
        time.sleep(1)
        print("end456")  # 第4步
    
    t1=Thread(target=foo)
    t2=Thread(target=bar)
    
    t1.daemon=True
    t1.start()
    t2.start()
    print("main-------")  # 第3步
    
    # 结果:
    # 123
    # 456
    # main-------
    # end456
    
    

8. 线程互斥锁

  • 多个任务公抢一个数据,保证数据的安全的目的,要让其串行

  • 代码示例

    from threading import Thread
    from threading import Lock
    import time
    import random
    
    x = 100
    def task(lock):
        lock.acquire()
        global x
        temp = x
        time.sleep(0.01)
        temp = temp - 1
        x = temp
        lock.release()
    
    if __name__ == '__main__':
        mutex = Lock()
        l1 = []
        for i in range(100):
            t = Thread(target=task,args=(mutex,))
            l1.append(t)
            t.start()
    
        for i in l1:
    		i.join()
        print(f'主线程{x}')
    
    
原文地址:https://www.cnblogs.com/liubing8/p/11403483.html