zoukankan      html  css  js  c++  java
  • 多进程与多线程(与文章相结合)

    线程:最小的执行单元
    进程:最小的资源管理单元


    切换的操作者:操作系统

    进程/线程切换原则:
    1、时间片
    2、遇到IO操作切换
    3、优先级切换


    IO密集型任务:程序存在大量IO操作

    计算密集型任务:程序存在大量计算操作


    对于PYTHON(无法利用多核):

    多线程处理 IO密集型任务具有优势
    计算密集型任务 不推荐使用多线程

    from multiprocessing import process
    构造方法:
    Process([group [, target [, name [, args [, kwargs]]]]])
      group: 线程组,目前还没有实现,库引用中提示必须是None;
      target: 要执行的方法;
      name: 进程名;
      args/kwargs: 要传入方法的参数。
    实例方法:
      is_alive():返回进程是否在运行。

      join([timeout]):阻塞当前上下文环境的进程程,直到调用此方法的进程终止或到达指定的timeout(可选参数)。
      start():进程准备就绪,等待CPU调度
      run():strat()调用run方法,如果实例进程时未制定传入target,这star执行t默认run()方法。
      terminate():不管任务是否完成,立即停止工作进程
    属性:
      daemon:和线程的setDeamon功能一样
      name:进程名字。
      pid:进程号。
    通过tasklist(Win)或者ps -elf |grep(linux)命令检测每一个进程号(PID)对应的进程名
    进程队列
    import multiprocessing
    def foo(q):
    q.put([11,'hello',True])
    if __name__ == '__main__': ###进程里都要有的
    q=multiprocessing.Queue()
    p=multiprocessing.Process(target=foo,args=(q,))
    p.start()
    print(q.get())
    进程管道
    from multiprocessing import Pipe,Process

    def foo(sk):
    sk.send('hello word')
    print(sk.recv())

    if __name__ == '__main__':
    sock,conn=Pipe()
    p=Process(target=foo,args=(sock,))
    p.start()

    print(conn.recv())
    conn.send('hi son')
    Pipe()返回的两个连接对象代表管道的两端。 每个连接对象都有send()和recv()方法(等等)。 请注意,如果两个进程(或线程)尝试同时读取或写入管道的同一端,管道中的数据可能会损坏。
    manager进程之间数据共享
    from multiprocessing import Process, Manager

    def foo(l,i):
    l.append(i*i)
    if __name__ == '__main__':
    manager=Manager()
    Mlist=manager.list([11,22,33])
    l=[]
    for i in range(5):
    p=Process(target=foo,args=(Mlist,i))
    p.start()
    l.append(p)
    for i in l:
    i.join()
    print(Mlist)
    进程池
    def foo(n):
    print(n)
    time.sleep(1)
    if __name__ == '__main__':
    pool_obj=Pool(5)
    for i in range(20):
    pool_obj.apply_async(func=foo,args=(i,))
    pool_obj.close()
    pool_obj.join()
    print('ending')
    进程池内部维护一个进程序列,当使用时,去进程池中获取一个进程,如果进程池序列中没有可供使用的进程,那么程序就会等待,直到进程池中有可用进程为止。
    进程池中有以下几个主要方法:
    apply:从进程池里取一个进程并执行
    apply_async:apply的异步版本
    terminate:立刻关闭线程池
    join:主进程等待所有子进程执行完毕,必须在close或terminate之后
    close:等待所有进程结束后,才关闭线程池


    在当前进程中开启子进程
    主进程重上到下代码执行完了就结束了的,就用守护进程

     

    什么是生产者消费者模型
    程序中有两类角色
    一类负责生产数据(生产者)
    一类负责处理数据(消费者)
    引入生产者消费者模型为了解决的问题是:
    平衡生产者与消费者之间的速度差
    如何实现:
    生产者-》队列--》消费者
    生产者消费者模型实现了程序的解耦和

    现代操作系统

    站在资源角度就是主进程
    站在执行角度就是主线程

     

    进程和线程的关系:

    (1)一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。
    (2)资源分配给进程,同一进程的所有线程共享该进程的所有资源。
    (3)CPU分给线程,即真正在CPU上运行的是线程。
    线程与进程的区别归纳:
    a.地址空间和其它资源:进程间相互独立,同一进程的各线程间共享。某进程内的线程在其它进程不可见。
    b.通信:进程间通信IPC,线程间可以直接读写进程数据段(如全局变量)来进行通信——需要进程同步和互斥手段的辅助,以保证数据的一致性。
    c.调度和切换:线程上下文切换比进程上下文切换要快得多。
    d.在多线程OS中,进程不是一个可执行的实体


    保护我们数据安全加的锁叫用户锁,1、解释器级别的GIL,2、用户级别的只给数据加,无关的不加
    互斥锁(同步锁(Lock))只能一次acquire(),一次release(),有一个线程拿到,其他谁都拿不到。
    在使用这种锁的时候,有时候不经意间,就会出现你在释放这两把锁的时候,再去拿,可能拿到一把,另一把再想用可能被别人拿到了,这时候大家相互等待,就造成了死锁,为了不放这种现象出现,就用一把递归锁。
    也就是在Lock前加一个R,threading.RLock()
    信号量:功能是为了实现链接延迟,实现了多个线程获取这把锁semaphore,可以限制最大连接数为5


    线程的一个关键特性是每个线程都是独立运行且状态不可预测。如果程序中的其 他线程需要通过判断某个线程的状态来确定自己下一步的操作,这时线程同步问题就 会变得非常棘手。为了解决这些问题,我们需要使用threading库中的Event对象
    event的四种方法
    event.isSet():返回event的状态值;
    event.wait():如果 event.isSet()==False将阻塞线程;
    event.set(): 设置event的状态值为True,所有阻塞池的线程激活进入就绪状态, 等待操作系统调度;
    event.clear():恢复event的状态值为False。


    import queue 存值数据是序列类型
    队列:线程安全的数据结构
    q.put() 放入 q.get()取出
    FIFO即First in First Out,先进先出。Queue提供了一个基本的FIFO容器,使用方法很简单,maxsize是个整数,指明了队列中能存放的数据个数的上限。一旦达到上限,插入会导致阻塞,直到队列中的数据被消费掉。如果maxsize小于或者等于0,队列大小没有限制。
    LIFO即Last in First Out,后进先出。

    如果当前一个join()正在阻塞,它将在队列中的所有任务都处理完时恢复执行(即每一个由put()调用入队的任务都有一个对应的task_done()调用)。

    join()
    阻塞调用线程,直到队列中的所有任务被处理掉。
    只要有数据被加入队列,未完成的任务数就会增加。当消费者线程调用task_done()(意味着有消费者取得任务并完成任务),未完成的任务数就会减少。当未完成的任务数降到0,join()解除阻塞。

    task_done()
    意味着之前入队的一个任务已经完成。由队列的消费者线程调用。每一个get()调用得到一个任务,接下来的task_done()调用告诉队列该任务已经处理完毕。

    q.qsize() 返回队列的大小
    q.empty() 如果队列为空,返回True,反之False
    q.full() 如果队列满了,返回True,反之False
    q.full 与 maxsize 大小对应
    q.get([block[, timeout]]) 获取队列,timeout等待时间
    q.get_nowait() 相当q.get(False)非阻塞
    q.put(item) 写入队列,timeout等待时间
    q.put_nowait(item) 相当q.put(item, False)
    q.task_done() 在完成一项工作之后,q.task_done() 函数向任务已经完成的队列发送一个信号
    q.join() 实际上意味着等到队列为空,再执行别的操作


    生产者消费模型:基于多线程编程
    生产者:创建数据的模型
    消费者:获取数据的模型
    优点:解耦合, 实现并发

    由于GIL的存在,python中的多线程其实并不是真正的多线程,如果想要充分地使用多核CPU的资源,在python中大部分情况需要使用多进程。
    multiprocessing包是Python中的多进程管理包。与threading.Thread类似,它可以利用multiprocessing.Process对象来创建一个进程。该进程可以运行在Python程序内部编写的函数。该Process对象与Thread对象的用法相同
    多进程的优缺点:
    优点:可以利用多核,实现并行运算
    缺点:1、开销太大 2、通信困难

    创建进程是在一个进程内,需要向操作系统发送请求,然后在开辟一块空间

  • 相关阅读:
    MySql学习20----数据库范式
    MySql学习17----数据库事务(01)
    MySql学习19-----用户管理
    MySql学习18----数据库事务---命令使用(02)
    MySql学习16----查看mysql库大小、表大小、索引大小
    MySql学习13----触发器
    MySql学习15----MySql日志
    java公开课-04-log4j
    java公开课-04-异常
    java公开课-03-内部类
  • 原文地址:https://www.cnblogs.com/sunxiansheng/p/7688511.html
Copyright © 2011-2022 走看看