zoukankan      html  css  js  c++  java
  • 并发编程,python的进程,与线程


    并发编程
    操作系统发展史
    基于单核研究
    多道技术
    1.空间上的复用
    多个程序公用一套计算机硬件
    2.时间上的复用
    切换+保存状态
    例子:洗衣 烧水 做饭

    切换
    1.程序遇到IO操作系统会立刻剥夺走CPU的执行权限
    IO:input,sleep,accept,recv...阻塞 日常生活中使用软件通常都是IO密集型

    2.当你的程序长时间占用CPU的时候也会被操作系统剥夺走cpu的执行权限


    进程理论
    进程调度
    时间片轮转法+多级反馈队列

    进程三状态图
    ps:程序不会立刻进入运行态 都会现在就绪态等待cpu的执行


    同步异步:指的是任务的提交方式
    同步:提交任务之后原地等待任务的返回结果 期间不做任何事
    异步:提交任务之后立刻执行下一行代码 不等待任务的返回结果 >>> 结果的获取使用异步回调机制
    阻塞非阻塞:指的是程序的运行状态
    阻塞:阻塞态
    非阻塞:就绪态或者是运行态


    创建进程的两种方式
    使用使用Process实例化
    继承Process类重写run方法
    ps:windows在开启进程的时候必须在__main__代码块内,因为windows是以模块导入的方式从上执行代码

    什么是进程:
    正在运行的程序
    一个进程对应到内存中就是一块独立的内存空间

    join方法
    主进程等待某个指定的子进程运行结束,不影响其他子进程的运行

    进程对象及其他方法
    current_process().pid
    os.getpid
    os.getppid
    terminate()
    is_alive()

    守护进程
    daemon
    这一句化必须在start之前使用

    进程间数据是隔离的

    互斥锁
    多个程序操作用一份数据的时候会出现数据错乱的现象
    如何避免:
    将操作数据的部分加锁处理
    会将并发变成串行牺牲了效率但是保证了数据的安全

    抢锁
    acquire()
    释放锁
    release()
    抢锁步骤是由操作系统决定的,即到底谁将会被分配到锁,进而可以被执行.
    只有当程序释放了锁,之后的程序才能继续去抢锁.




    进程间通信IPC机制
    一般进程间通信会使用队列这一数据类型来进行数据通信.
    ps:

    """
    队列:先进先出
    堆栈:先进后出
    """
    示例:
    from multiprocessing import Queue


    q = Queue(5) # 括号内可以传参数 表示的是这个队列的最大存储数
    # 往队列中添加数据
    q.put(1)
    q.put(2)
    # print(q.full()) # 判断队列是否满了
    q.put(3)
    q.put(4)
    q.put(5)
    # print(q.full())
    # q.put(6) # 当队列满了之后 再放入数据 不会报错 会原地等待 直到队列中有数据被取走(阻塞态)

    print(q.get())
    print(q.get())
    print(q.get())
    print(q.empty()) # 判断队列中的数据是否取完
    print(q.get())
    print(q.get())
    print(q.empty())
    # print(q.get_nowait()) # 取值 没有值不等待直接报错
    # print(q.get()) # 当队列中的数据被取完之后 再次获取 程序会阻塞 直到有人往队列中放入值
    """
    full
    get_nowait
    empty
    都不适用于多进程的情况
    """
    from multiprocessing import Process,Queue

    def producer(q):
    q.put('hello GF~')

    def consumer(q):
    print(q.get())

    if __name__ == '__main__':
    q = Queue()
    p = Process(target=producer,args=(q,))
    c = Process(target=consumer, args=(q,))
    p.start()
    c.start()


    """
    子进程放数据 主进程获取数据
    两个子进程相互放 取数据
    """


    生产者消费者模型
    """
    生产者:生产/制造数据的
    消费者:消费/处理数据的
    这其中有时会出现下列情况
    用大白话讲:做包子的,买包子的
    1.做包子远比买包子的多
    2.做包子的远比包子的少
    供求不平衡的问题
    """
    代码示例:

    方法一:

    使用了jionableQueue方法,

    方法二:
    #手动为每个进程设置等待:

    from multiprocessing import Process,Queue,joinableQueue
    import random , time
    def producer(name, food ,q):
    for i in range(4)
    data = f'{name}生产了{food},第{i+1}号'
    time.sleep(random.random()) # 生产间隔
    q.put(data)
    print(data)

    def consumer (name,q):
    while True:
    data=q.get()
    if data == None :
    print('吃完了,也做完了')
    break
    print(f'{name}生产了{data})
    time.sleep(random.random() #消化时间
    q.task_done() # 告诉队列已经从队列中取出了一个数据,并且已经处理完毕

    if __name__ == '__main__':
    q = JoinableQueue() #创建对象

    p = Process(target=producer,args=('大厨egon','馒头',q))
    p1 = Process(target=producer,args=('墩子tank','生蚝',q))
    # c = Process(target=consumer,args=('小小',q))
    c1 = Process(target=consumer,args=('吃货',q))
    p.start()
    p1.start()
    # c.daemon = True
    c1.daemon = True
    # c.start()
    c1.start()
    p.join()
    p1.join()

    q.join() # 等到队列中数据全部取出
    # q.put(None)
    # q.put(None)


    结果如下:
    墩子tank生产了生蚝.5号
    吃货吃了墩子tank生产了生蚝.5号
    大厨egon生产了馒头.4号
    吃货吃了大厨egon生产了馒头.4号
    墩子tank生产了生蚝.2号
    大厨egon生产了馒头.3号
    大厨egon生产了馒头.9号
    吃货吃了墩子tank生产了生蚝.2号
    大厨egon生产了馒头.1号
    墩子tank生产了生蚝.7号
    吃货吃了大厨egon生产了馒头.3号
    吃货吃了大厨egon生产了馒头.9号
    吃货吃了大厨egon生产了馒头.1号
    墩子tank生产了生蚝.8号
    吃货吃了墩子tank生产了生蚝.7号
    吃货吃了墩子tank生产了生蚝.8号


    Process finished with exit code 0


    线程理论


    什么是线程
    进程线程其实都是虚拟单位,都是用来帮助我们形象的描述某种事物

    进程:资源单位
    线程:执行单位
    将内存比如成工厂
    那么进程就相当于是工厂里面的车间
    而你的线程就相当于是车间里面的流水线
    ps:每个进程都自带一个线程,线程才是真正的执行单位,进程只是在线程运行过程中
    提供代码运行所需要的资源


    为什么要有线程
    开进程
    1.申请内存空间 耗资源
    2."拷贝代码" 耗资源

    开线程
    一个进程内可以起多个线程,并且线程与线程之间数据是共享的
    ps:开启线程的开销要远远小于开启进程的开销

    from threading import Thread
    import time

    def task(name):
    print('%s is running'%name)
    time.sleep(3)
    print('%s is over'%name)
    # 开线程不需要在__main__代码块内 但是习惯性的还是写在__main__代码块内
    t = Thread(target=task,args=('egon',))
    t.start() # 告诉操作系统开辟一个线程 线程的开销远远小于进程
    # 小的代码执行完 线程就已经开启了
    print('主线程')

    from threading import Thread
    import time

    class MyThread(Thread):
    def __init__(self,name):
    super().__init__()
    self.name = name

    def run(self):
    print('%s is running'%self.name)
    time.sleep(3)
    print('%s is over'%self.name)

    t = MyThread('egon')
    t.start()
    print('主线程')

    执行结果:
    egon is running

    egon is running

    egon is over
    egon is over


    join方法

    from threading import Thread,current_thread,active_count
    import time
    import os

    def task(name,i):
    print('%s is running'%name)
    # print('子current_thread:',current_thread().name)
    # print('子',os.getpid())
    time.sleep(i)

    print('%s is over'%name)
    # 开线程不需要在__main__代码块内 但是习惯性的还是写在__main__代码块内
    t = Thread(target=task,args=('egon',1))
    t1 = Thread(target=task,args=('jason',2))
    t.start() # 告诉操作系统开辟一个线程 线程的开销远远小于进程
    t1.start() # 告诉操作系统开辟一个线程 线程的开销远远小于进程
    t1.join() # 主线程等待子线程运行完毕
    print('当前正在活跃的线程数',active_count())
    # 小的代码执行完 线程就已经开启了
    print('主')
    # print('主current_thread:',current_thread().name)
    # print('主',os.getpid())


    线程间通信
    注意 与进程不同的是 ,同一进程下的多线程是共用该进程下的资源的,

    即他们之间使用的可以说是同一名称空间,

    from threading import Thread
    money = 666
    def test():
    global money
    money = 999
    t = Thread(target=task)
    t.start()
    t.join()
    print(money)
    执行结果:
    999
    线程对象及其他方法
    from threading import Thread,current_thread
    import time

    守护线程:

    def task(i):
    print(current_thread().name)
    time.sleep(i)
    print('GG')
    # for i in range(3):
    # t = Thread(target=task,args=(i,))
    # t.start()
    t = Thread(target=task,args=(1,))
    t.daemon = True
    t.start()
    print('主')
    # 主线程运行结束之后需要等待子线程结束才能结束呢?
    """
    主线程的结束也就意味着进程的结束
    主线程必须等待其他非守护线程的结束才能结束
    (意味子线程在运行的时候需要使用进程中的资源,而主线程一旦结束了资源也就销毁了)
    """


    举例子:

    from threading import Thread
    from multiprocessing import Process
    import time
    def foo():
    print(123)
    time.sleep(1)
    print("end123")

    def bar():
    print(456)
    time.sleep(3)
    print("end456")

    def run():
    print('789')
    time.sleep(2)
    print('end789')

    if __name__ == '__main__':
    t1=Thread(target=foo)
    t2=Thread(target=bar)
    t3= Thread(target=run)
    t2.daemon=True
    t1.start()
    t2.start()
    t3.start()
    print("main-------开始")
    执行结果:
    123
    456
    789
    main-------开始
    end123
    end789


    可以看到 尽管我们使用了守护进程,即将主线程设置为t2的守护进程,当主线程死亡,则t2被强制杀死,那么结果就不包含'end456'.

    线程互斥锁:
    与操作进程互斥锁同理,当我们想要安全的增删改查数据,最后对操作数据的步骤加锁,以保证数据的安全性,操作和进程相同,即创建锁,抢锁,释放锁,

    from threading import Thread,Lock
    import time


    n = 77

    def task(mutex):
    global n
    mutex.acquire() # 抢锁
    tmp = n
    time.sleep(0.1)
    n = tmp - 1
    mutex.release() # 释放锁

    t_list = []
    mutex = Lock() # 示例化一个锁,即创建一个锁
    for i in range(77):
    t = Thread(target=task,args=(mutex,))
    t.start()
    t_list.append(t)
    for t in t_list:
    t.join()
    print(n)

  • 相关阅读:
    100LS练习方法
    甲方开源项目
    linux端口转发方式
    内网的一些穿透工具
    Checkpoint防火墙(一)
    2020目标
    Linux逻辑卷的扩容和缩小
    linux挂载磁盘
    Selinux介绍
    以太坊私链搭建
  • 原文地址:https://www.cnblogs.com/Sunbreaker/p/11340703.html
Copyright © 2011-2022 走看看