多进程与多线程(与文章相结合)

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


切换的操作者:操作系统

进程/线程切换原则:
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、通信困难

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

原文地址:https://www.cnblogs.com/sunxiansheng/p/7688511.html