• day37


    今日内容概要

    • 进程锁

    • 进程之间数据隔离问题

    • 如何查看进程的进程号

    • 队列(Queue类,专业的队列)

    • 生产者消费者模型

    • 线程理论

    • 如何开启线程(Thread)

    • 线程类的参数、方法、属性

    • 守护线程(守护进程)

    • GIL全局解释器锁

    进程锁

    1. """锁在IT界都是非常重要的,不但在Python中出现,尤其是数据库中得锁更多,比如:表锁、行锁、悲观锁、乐观锁、进程锁、互斥锁、递归锁、可重入锁、死锁等"""
    2. # 使用锁的目的就是为了保证安全!
    3. import time
    4. from multiprocessing import Process, Lock
    5. def task(i, lock):
    6. # 上一把锁
    7. lock.acquire()
    8. print("进程%s来了" % i)
    9. time.sleep(1)
    10. print("进程%s走了" % i)
    11. # 释放锁
    12. lock.release()
    13. """只要你上了锁,一定别忘了最后释放锁,否则的话,别的进程永远进不来"""
    14. # 加锁一定好码? 虽然保证了数据的安全,但是呢,执行的效率一定是降低了
    15. # 有些场景该加锁的时候一定要加锁,
    16. if __name__ == '__main__':
    17. lock=Lock() # 得到一把锁
    18. for i in range(3):
    19. p = Process(target=task, args=(i+1, lock))
    20. p.start()

    如何查看进程号

    1. import time
    2. import os
    3. from multiprocessing import Process, Lock
    4. """有了进程号,我们就可以通过进程号来结束进程的执行 kill 9176 kill -9 9176"""
    5. # taskkill /pid {pid}
    6. def task():
    7. print("task进程的进程号:", os.getpid()) # os.getpid() 写在哪个进程里面就会输出哪个进程的进程号
    8. print("task进程的父进程的进程号:", os.getppid()) # parent process
    9. import time
    10. time.sleep(20)
    11. if __name__ == '__main__':
    12. p=Process(target=task, )
    13. p.start()
    14. print('子进程的进程号:', p.pid)
    15. print("主进程的进程号", os.getpid())
    16. time.sleep(10)

    进程之间数据隔离问题

    1. n=100
    2. def task():
    3. global n
    4. n=1
    5. print("子进程")
    6. from multiprocessing import Process
    7. """这两个进程之间的数据有没有通信? 么有通信"""
    8. if __name__ == '__main__':
    9. p = Process(target=task)
    10. p.start()
    11. """先让子进程先执行,让子进程去改值"""
    12. p.join()
    13. print("主进程中得值:", n)
    14. """问题是:如何让进程与进程之间数据通信? """

    队列(Queue)

    概念介绍----multiprocess.Queue

    创建共享的进程队列,Queue是多进程安全的队列,可以使用Queue实现多进程之间的数据传递
    Queue([maxsize])创建共享的进程队列
    参数:maxsize是队列中允许的最大项数。如果省略此参数,则无大小限制。底层队列使用管道和锁定实现

    方法介绍

    q.get:返回q中的一个项目。如果q为空,此方法将阻塞,直到队列中有项目可用为止。block用于控制阻塞行为,默认为True,如果设置为False,将引发Queue.Empty异常(定义在Queue模块中)。timeout是可选超时时间,用在阻塞模式中。如果在制定的时间间隔内没有项目变为可用,将引发Queue.Empty异常。

    q.get_nowait():同q.get(False)

    q.put:将item放入队列。如果队列已满,此方法将阻塞至有空间可用为止。

    q.qsize():返回队列中目前项目的正确数量。此函数的结果并不可靠。

    q.empty():如果调用此方法时q为空,返回True。

    q.full():如果q已满,返回True。由于线程的存在,结果也是不可靠的

    代码实例

    单看队列用法
    1. '''
    2. multiprocessing模块支持进程间通信的两种主要形式:管道和队列
    3. 都是基于消息传递实现的,但是队列接口
    4. '''
    5. from multiprocessing import Queue
    6. q=Queue(3)
    7. #put ,get ,put_nowait,get_nowait,full,empty
    8. q.put(3)
    9. q.put(3)
    10. q.put(3)
    11. # q.put(3) # 如果队列已经满了,程序就会停在这里,等待数据被别人取走,再将数据放入队列。
    12. # 如果队列中的数据一直不被取走,程序就会永远停在这里。
    13. try:
    14. q.put_nowait(3) # 可以使用put_nowait,如果队列满了不会阻塞,但是会因为队列满了而报错。
    15. except: # 因此我们可以用一个try语句来处理这个错误。这样程序不会一直阻塞下去,但是会丢掉这个消息。
    16. print('队列已经满了')
    17. # 因此,我们再放入数据之前,可以先看一下队列的状态,如果已经满了,就不继续put了。
    18. print(q.full()) #满了
    19. print(q.get())
    20. print(q.get())
    21. print(q.get())
    22. # print(q.get()) # 同put方法一样,如果队列已经空了,那么继续取就会出现阻塞。
    23. try:
    24. q.get_nowait(3) # 可以使用get_nowait,如果队列满了不会阻塞,但是会因为没取到值而报错。
    25. except: # 因此我们可以用一个try语句来处理这个错误。这样程序不会一直阻塞下去。
    26. print('队列已经空了')
    27. print(q.empty()) #空了

    上面这个例子还没有加入进程通信,只是先来看看队列为我们提供的方法,以及这些方法的使用和现象

    子进程发送数据给父进程
    1. import time
    2. from multiprocessing import Process, Queue
    3. def f(q):
    4. q.put([time.asctime(), 'from Eva', 'hello']) #调用主函数中p进程传递过来的进程参数 put函数为向队列中添加一条数据。
    5. if __name__ == '__main__':
    6. q = Queue() #创建一个Queue对象
    7. p = Process(target=f, args=(q,)) #创建一个进程
    8. p.start()
    9. print(q.get())
    10. p.join()

    上面是一个queue的简单应用,使用队列q对象调用get函数来取得队列中最先进入的数据。接下来看一个稍微复杂一些的例子:批量生产数据放入队列再批量获得结果

    批量生产数据放入队列再批量获取结果
    1. import os
    2. import time
    3. import multiprocessing
    4. # 向queue中输入数据的函数
    5. def inputQ(queue):
    6. info = str(os.getpid()) + '(put):' + str(time.asctime())
    7. queue.put(info)
    8. # 向queue中输出数据的函数
    9. def outputQ(queue):
    10. info = queue.get()
    11. print ('%s%s%s'%(str(os.getpid()), '(get):',info))
    12. # Main
    13. if __name__ == '__main__':
    14. multiprocessing.freeze_support()
    15. record1 = [] # store input processes
    16. record2 = [] # store output processes
    17. queue = multiprocessing.Queue(3)
    18. # 输入进程
    19. for i in range(10):
    20. process = multiprocessing.Process(target=inputQ,args=(queue,))
    21. process.start()
    22. record1.append(process)
    23. # 输出进程
    24. for i in range(10):
    25. process = multiprocessing.Process(target=outputQ,args=(queue,))
    26. process.start()
    27. record2.append(process)
    28. for p in record1:
    29. p.join()
    30. for p in record2:
    31. p.join()

    生产者消费者模型

    在并发编程中使用生产者和消费者模式能够解决绝大多数并发问题。该模式通过平衡生产线程和消费线程的工作能力来提高程序的整体处理数据的速度

    基于队列实现生产者消费模型
    1. from multiprocessing import Process,Queue
    2. import time,random,os
    3. def consumer(q):
    4. while True:
    5. res=q.get()
    6. time.sleep(random.randint(1,3))
    7. print('%s 吃 %s' %(os.getpid(),res))
    8. def producer(q):
    9. for i in range(10):
    10. time.sleep(random.randint(1,3))
    11. res='包子%s' %i
    12. q.put(res)
    13. print('生产了 %s' %(os.getpid(),res))
    14. if __name__ == '__main__':
    15. q=Queue()
    16. #生产者们:即厨师们
    17. p1=Process(target=producer,args=(q,))
    18. #消费者们:即吃货们
    19. c1=Process(target=consumer,args=(q,))
    20. #开始
    21. p1.start()
    22. c1.start()
    23. print('主')

    线程

    1. 在一个进程中,线程就是必须存在的,至少要有一个线程来执行任务
    2. 一个进程中可以有多个线程,在一个进程中可有开启多个线程来执行任务
    3. 进程和线程都是有操作系统调度的
    4. 进程是操作系统分配资源的基本单位,线程是操作系统执行的最小单位
    5. 如何开启线程? 跟开进程是一样的
    6. import time
    7. def task(a, b):
    8. print("from task")
    9. time.sleep(2)
    10. print("aaa")
    11. from multiprocessing import Process
    12. from threading import Thread
    13. import threading
    14. if __name__ == '__main__':
    15. # p=Process(target=task)
    16. # p.start()
    17. # deamon
    18. """开线程的资源非常小,以至于代码走到这一行就立马开起来了,所以就会立刻执行"""
    19. t=Thread(target=task, name='Thread-2', args=('a', ), kwargs={'b':1})
    20. # t.setDaemon(True)
    21. """设置为了守护进程:主进程结束,子进程跟着结束"""
    22. t.daemon = True # 把该线程设置为守护线程, 主线程执行完毕,子线程也结束
    23. """守护线程有什么用?"""
    24. t.start()
    25. # t.join()
    26. # print(t.name)
    27. """一个进程中如果只有一个线程,该线程称之为是主线程,其他线程称之为是子线程"""
    28. t.is_alive()
    29. print(t.getName()) # t.name
    30. t.setName('aaaa')
    31. # t.name = 'aaa'
    32. print("主线程")
    33. print(threading.currentThread())
    34. print(threading.enumerate())
    35. print(threading.activeCount())

  • 相关阅读:
    git 版本管理
    11.22Spring 学习day02
    【数据结构与算法】之递归算法
    Qt学习总结之布局管理
    2022中国机器人产业报告发布 企业如何应对新阶段下的增长与竞争?
    uniapp一键生成iOS通用链接
    遗传算法求解旅行商问题(含python源代码)
    Vue项目大屏可视化适配方法记录
    (附源码)基于spring boot 房屋租赁系统小程序-计算机毕设 88306
    性能测试——App性能测试需要关注的指标
  • 原文地址:https://blog.csdn.net/qq_67257222/article/details/133905710