zoukankan      html  css  js  c++  java
  • day 36

    生产者消费者补充

    def make_hot_dog(name,q):
        for i in range(5):
            time.sleep(random.randint(1,3))
            print("%s 生产了di%s热狗"%(name,i))
            q.put("%s的第%s个热狗"%(name,i))       #将生产好的数据放入共享对列中
    
    def eat(name,q):
        while True:
            hot_dog = q.get()         #将数据从共享队列中取出操作
            time.sleep(random.randint(1,2))
            print("%s吃了%s"%(name,hot_dog))
            q.task_done()            # 因为实在JoinableQueue中所以每取出一次数据后就用使用一次task_done()
                                    #要与下方.join对应起来
    
    if __name__ == '__main__':
        q = JoinableQueue()        #在主进程中设置一个公共的共享队列
        p = Process(target=make_hot_dog,args=("藤原热狗店",q))
        p.start()
    
        e = Process(target=eat,args=("阿花",q))
        # e.daemon = True
        e.daemon = True       #此处使用daemon将e进程设置为守护进程,是因为,此处消费者进程没有结束条件,当生产者结束以后
                              #消费者把队列中的数据取完以后,没有数据可取以后,程序又不会结束,就会卡在那里
        e.start()
        # e.daemon = True
        p.join()          #此处使用join是为了让主进程等待子进程结束,如果不加的话,主进程
                          # 主进程结束守护进程也会结束掉
        print("热狗做好了")
        q.join()          # 这里使用.join的是为了回应给主程序,消费者将队列中的数据取完了,通知之进程向下执行
        print("阿花吃完了")

    什么是线程

    线程是操作系统最小的运算调度单位,是存在与进程中的,一个线程就是一个固定的执行流程。

    线程的进程的关系

    线程是存在与内存中的,并不能单独存在,进程是一个资源单位,它包含了程序运行所需要的所有资源。

    真正运行程序代码的是线程,如果没有线程,进程中的资源无法被利用起来,进程中至少会有一个线程,创建进程时

    产生的线程被称为主线程,当我们启动程序时操作系统就会给进程创建一个主线程,我们自己因任务需要创建的线程被称为
    子线程。

    为什么需要线程

    使用线程的目的只要一个,就是为了提高效率。可以把工厂车间看作是一个进程,在没有接触过线程时我们为了提高效率

    会再创建一个或多个进程,来提高效率,有了线程后就不会这么想了,直接在进程里创建新的线程即可,就好像,工厂里为了提高

    生产效率,肯定是能在车间例加设流水线而不是再建一个车间出来。

    如何使用

    使用方式上和使用进程基本相同,和线程不同的是开启线程的代码可以放在任意位置,不必放在判断下面,因为线程是在进程内部

    执行代码,并不是像开启进程一样去创建一个新的内存空间。

    from multiprocessing import Process
    from threading import Thread,current_thread,Lock
    
    #
    #
    def task():
        print("1",current_thread())     #子线程
        print("111111111")
    
    
    
    # if __name__ == "__main__":
    #只是在同一个进程内执行了另外一行代码,不加判断也可以
    t = Thread(target=task)
    t.start()
    print("2222",current_thread())    #主线程
    
    
    
    
    方法2:创建新的thread类并重新覆盖run方法
    class MyThread(Thread):
        def run(self):
            print("445464654")
    
    
    m = MyThread()
    m.start()

    线程的特点

    创建线程的开销小,多个进程之间不存在子符关系,所有线程的PID都是相同的。同一个进程中的多个线程的数据是共享的。

    # 同一个进程中线程间资源共享
    a = 20
    def task():
        global a
        print("子线程run>>>>>")
        a = 100
    t1 = Thread(target=task)
    t1.start()
    t1.join()      #等待子进程执行结束,确保数据已被修改
    print(a)

    线程 互斥锁

    同一个进程内的线程资源共享带来的问题就是资源的抢夺,线程中也会存在安全的问题,多线程可以并发执行,一旦执行,访问同一个同一个进程中的资源就会出现数据错乱的问题。

    解决数据错乱和安全的问题的方案还是互斥锁。

    # 线程的安全问题
    num = 10
    l = Lock()
    def task():
        global num
        l.acquire()
        a = num
        time.sleep(1)
        num = a-1
        l.release()
    
    if __name__ == "__main__":
        for i in range(10):
            t = Thread(target=task)
            t.start()
        for t in enumerate()[1:]:    #用于查看线程总数我们需要的是子线程,这里将主线程给通过切片拿出去
            print(t)
            t.join()     #等待所有子线程结束
        print(num

    死锁问题

    死锁是指程序中出现了不止一把锁,分别被不同的线程持有,有一个资源如果想要使用必须两把锁同时持有,这样的话程序就会进入卡死状态,这就是死锁。

    例如

    要吃饭 必须具备盘子和筷子 但是一个人拿着盘子 等筷子 另一个人拿着筷子等盘子
    如何避免死锁问题 :
    锁不要有多个,一个足够
    如果真的发生了死锁问题,必须迫使一方先交出锁

    import time
    # 盘子
    lock1 = Lock()
    
    # 筷子
    lock2 = Lock()
    
    def eat1():
        lock1.acquire()
        print("%s抢到了盘子" % current_thread().name)
        time.sleep(0.5)
        lock2.acquire()
        print("%s抢到了筷子" % current_thread().name)
    
        print("%s开吃了!" % current_thread().name)
        lock2.release()
        print("%s放下筷子" % current_thread().name)
    
        lock1.release()
        print("%s放下盘子" % current_thread().name)
    
    
    def eat2():
        lock2.acquire()
        print("%s抢到了筷子" % current_thread().name)
    
        lock1.acquire()
        print("%s抢到了盘子" % current_thread().name)
    
    
        print("%s开吃了!" % current_thread().name)
    
    
        lock1.release()
        print("%s放下盘子" % current_thread().name)
        lock2.release()
        print("%s放下筷子" % current_thread().name)
    
    
    t1 = Thread(target=eat1)
    
    
    t2 = Thread(target=eat2)
    
    t1.start()
    t2.start()

    可重入锁

    Rlock 称之为递归锁或者可重入锁

    Rlock不是用来解决死锁问题的

    与Lock唯一的区别: Rlock同一线程可以多次执行acquire 但是执行几次acquire就应该对应release几次 如果一个线程已经执行过acquire 其他线程将无法执行acquire

     

    from threading import RLock, Lock, Thread
    
    # l = Lock()
    #
    # l.acquire()
    # print("1")
    # l.acquire()
    # print("2")
    
    
    l = RLock()
    
    # l.acquire()
    # print("1")
    # l.acquire()
    # print("2")
    
    def task():
        l.acquire()
        print("子run......")
        l.release()
    
    
    # 主线程锁了一次
    l.acquire()
    l.acquire()
    
    l.release()
    l.release()
    t1 = Thread(target=task)
    t1.start()

    信号量


    可以现在被锁定的代码 同时可以被多少线程并发访问。
    Lock 锁住一个马桶 同时只能有一个。
    Semaphore 锁住一个公共厕所 同时可以来一堆人。

    用途:仅用于控制并发访问 并不能防止并发修改造成的问题

    from threading import Semaphore, Thread
    import time
    
    s = Semaphore(5)
    def task():
        s.acquire()
        print("子run")
        time.sleep(3)
        print("子over")
        s.release()
    
    for i in range(10):
        t = Thread(target=task)
        t.start
  • 相关阅读:
    Django和flask中使用原生SQL方法
    UnicodeDecodeError: 'ascii' codec can't decode byte 0xe6 in
    Docker常用命令
    MySQL的压力测试
    使用docker-compose快速搭建PHP开发环境
    Docke如何配置Nginx和PHP
    Docker容器的重命名和自动重启
    docker部署MySQL、Redis和Nginx
    docker-compose的安装卸载以及如何使用
    docker如何制作自己的镜像
  • 原文地址:https://www.cnblogs.com/1624413646hxy/p/10974853.html
Copyright © 2011-2022 走看看