python之多进程与多线程

1、基本概念

1.1、线程

   线程:是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。一个线程是一个cpu执行时所需要的一串指令。
   线程的工作方式:CPU会给你一个在同一时间能够做多个运算的幻觉,实际上它在每个运算上只花了极少的时间,本质上CPU同一时刻只干了一件事,但是多个运算共同使用一块CPU,因为CPU可以存储执行每个运算的执行内容,再次执行时可以继续。

1.2、进程

   进程:一个程序的执行实例就是一个进程。每一个进程提供执行程序所需的所有资源。(进程本质上是资源的集合)。
   一个进程有一个虚拟的地址空间、可执行的代码、操作系统的接口、安全的上下文(记录启动该进程的用户和权限等等)、唯一的进程ID、环境变量、优先级类、最小和最大的工作空间(内存空间),还要有至少一个线程。每一个进程启动时都会最先产生一个线程,即主线程。然后主线程会再创建其他的子线程。
   进程资源有:内存页(同一个进程中的所有线程共享同一个内存空间),文件描述符(e.g. open sockets),安全凭证(e.g.启动该进程的用户ID)。

1.3、线程和进程的区别

   1.同一个进程中的线程共享同一内存空间,但是进程之间是独立的。
   2.同一个进程中的所有线程的数据是共享的(进程通讯),进程之间的数据是独立的。
   3.对主线程的修改可能会影响其他线程的行为,但是父进程的修改(除了删除以外)不会影响其他子进程。
   4.线程是一个上下文的执行指令,而进程则是与运算相关的一簇资源。
   5.同一个进程的线程之间可以直接通信,但是进程之间的交流需要借助中间代理来实现。
   6.创建新的线程很容易,但是创建新的进程需要对父进程做一次复制。
   7.一个线程可以操作同一进程的其他线程,但是进程只能操作其子进程。
   8.线程启动速度快,进程启动速度慢(但是两者运行速度没有可比性)。

2、python多线程(threading类)

2.1、GIL

   在非python环境中,单核情况下,同时只能有一个任务执行。多核时可以支持多个线程同时执行。但是在python中,无论有多少核,同时只能执行一个线程。究其原因,这就是由于GIL的存在导致的。
   GIL:全称是Global Interpreter Lock(全局解释器锁),某个线程想要执行,必须先拿到GIL,并且在一个python进程中,GIL只有一个。拿不到通行证的线程,就不允许进入CPU执行。GIL只在cpython中才有,因为cpython调用的是c语言的原生线程,所以他不能直接操作cpu,只能利用GIL保证同一时间只能有一个线程拿到数据。而在pypy和jpython中是没有GIL的。

2.1、Python多线程的工作过程

   拿到公共数据,申请GIL,python解释器调用系统原生线程,系统操作cpu执行运算,当该线程执行时间到后,无论运算是否已经执行完,系统都被要求释放,进而由其他线程重复上面的过程,等其他线程执行完后,又会切换到之前的线程(从他记录的上下文继续执行),整个过程是每个线程执行自己的运算,当执行时间到就进行切换(context switch)。
   注意:python想要充分利用多核CPU,就用多进程。因为每个进程有各自独立的GIL,互不干扰,这样就可以真正意义上的并行执行,在python中,多进程的执行效率优于多线程(仅仅针对多核CPU而言)。

2.2、线程锁

   线程锁:防止出现脏数据,线程锁,即同一时刻允许一个线程执行操作。线程锁用于锁定资源,你可以定义多个锁, 当你需要独占某一资源时,任何一个锁都可以锁这个资源,就好比你用不同的锁都可以把相同的一个门锁住是一个道理。
   互斥锁(Lock):互斥锁同时只允许一个线程更改数据。
   递归锁(RLcok):RLcok类的用法和Lock类一模一样,但它支持嵌套,,在多个锁没有释放的时候一般会使用使用RLcok类。
   信号量(BoundedSemaphore类):,Semaphore是同时允许一定数量的线程更改数据 。

2.3、事件,条件,定时器

   事件(Event类):python线程的事件用于主线程控制其他线程的执行。
   条件(Condition类):使得线程等待,只有满足某条件时,才释放n个线程。
   定时器(Timer类):定时器,指定n秒后执行某操作。

3、python多进程(multiprocessing类)

   多进程:在linux中,每个进程都是由父进程提供的。每启动一个子进程就从父进程克隆一份数据,但是进程之间的数据本身是不能共享的。

3.1进程通信

   由于进程之间数据是不共享的,所以不会出现多线程GIL带来的问题。多进程之间的通信通过Queue()或Pipe()来实现。

3.1.1 Queue()

   进程加入队列

3.1.2Pipe()

   Pipe的本质是进程之间的数据传递,而不是数据共享,这和socket有点像。pipe()返回两个连接对象分别表示管道的两端,每端都有send()和recv()方法。如果两个进程试图在同一时间的同一端进行读取和写入那么,这可能会损坏管道中的数据。

3.2、Manager

   通过Manager可实现进程间数据的共享。Manager()返回的manager对象会通过一个服务进程,来使其他进程通过代理的方式操作python对象。

3.3、进程锁(进程同步)

   数据输出的时候保证不同进程的输出内容在同一块屏幕正常显示,防止数据乱序的情况。

3.4、进程池

   由于进程启动的开销比较大,使用多进程的时候会导致大量内存空间被消耗。为了防止这种情况发生可以使用进程池,(由于启动线程的开销比较小,所以不需要线程池这种概念,多线程只会频繁得切换cpu导致系统变慢,并不会占用过多的内存空间)
   进程池中常用方法:apply() 同步执行(串行),apply_async() 异步执行(并行),terminate() 立刻关闭进程池,join() 主进程等待所有子进程执行完毕。必须在close或terminate()之后,close() 等待所有进程结束后,才关闭进程池。

4、协程(greenlet)

   线程和进程的操作是由程序触发系统接口,最后的执行者是系统,它本质上是操作系统提供的功能。
   协程:而协程的操作则是程序员指定的,在python中通过yield,人为的实现并发处理。
   协程存在的意义:对于多线程应用,CPU通过切片的方式来切换线程间的执行,线程切换时需要耗时。协程,则只使用一个线程,分解一个线程成为多个“微线程”,在一个线程中规定某个代码块的执行顺序。
   协程的适用场景:当程序中存在大量不需要CPU的操作时(IO)。常用第三方模块gevent和greenlet。(本质上,gevent是对greenlet的高级封装,因此一般用它就行,这是一个相当高效的模块。)
   实际上,greenlet就是通过switch方法在不同的任务之间进行切换。

import multiprocessing as mp
def worker_1():
    algorithm1.run(9200)
    solutions1 = algorithm1.result
    return solutions1

def worker_2():
    algorithm2.run(10000) 
    solutions2 = algorithm2.result
    return solutions2

def worker_3():
    algorithm3.run(10000) 
    solutions3 = algorithm3.result
    return solutions3

fun_list = [worker_1,worker_2,worker_3]
def multicore():
    pool=mp.Pool(processes=3)#定义一个Pool,默认为CPU核心数,可以自己定义多少个定义容量为3的进程池,最大三个子进程
    multi_res = []
    for fun in fun_list:
        res=pool.apply_async(fun)   # apply_async向进程池提交目标请求
        multi_res.append(res)#多个结果
    results_t = [res.get()for res in multi_res]
    solutions1 = results_t[0]
    solutions2 = results_t[1]
    solutions3 = results_t[2]
    return solutions1,solutions2,solutions3
if __name__=='__main__':
    solutions1, solutions2, solutions3 = multicore()
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值