进程与线程的初步了解
计算机系统中的各类任务,例如打开word,播放器,均有各自的进程(Process)。进程是资源分配的最小单位。
而一个进程至少拥有一个线程,是一对多的关系。例如,播放电影的进程中,音频和视频是同步播放的,它们是两个子任务,即为隶属于统一进程的不同线程(Thread)。线程是程序执行的最小单位。
每个线程可以共享进程的内存,且单独拥有线程栈来存储数据。
并发与并行的区别
多线程和多进程最重要的一个特点就是并发和并行。简单来说,多个任务在单核cpu上进行,通过各种进程调度规则(例如先来先服务,时间片轮转法等)实现一种宏观的同时进行(实际上是交替进行的),即为并发。
而在多核cpu上,任务数量少于核数时,可以做到真正的同时进行,即为并行。
并发和并行在系统中都很常见,多线程任务采用并发还是并行由计算机分配。
python实现多进程
Linux下Python多进程展示
linux下独有的fork()函数,可以根据父进程复制一个子进程,其中父进程返回子进程的id,子进程返回0。
import os
#输出父进程的id
print('%s process start'%os.getpid())
#fork()函数生成子进程
pid=os.fork()
#子进程的返回结果
if pid==0:
#getpid()和getppid()分别返回当前进程id和父进程的id
print('I am %s line and my parent is %s process'%(os.getpid(),os.getppid()))
#父进程的返回结果
else:
print('I (%s) just created a child process (%s).' % (os.getpid(), pid))
18740 process start
I 18740 just created a child process 18741.
I am 18741 line and my parent is 18740 process
Windows下Python多进程展示
windows下没有fork()函数,可以使用multiprocessing模块。
import os
from multiprocessing import Process
def func(name):
#输出当前进程的id
print('%s child process %s start'%(name,os.getpid()))
if __name__=='__main__':
#输出父进程的id
print('Parent process %s start'%os.getpid())
#以定义好的函数创建进程,并指定参数
p=Process(target=func,args=('study',))
#子进程开始
p.start()
#join()函数确保该子进程执行完毕后才继续进行
p.join()
print('All processes finished')
Parent process 9120 start
study child process 16612 start
All processes finished
使用multiprocessing中的Pool方法,建立一个进程池
p=Pool(n)
参数n为进程池中可容纳的最大进程数。
使用进程池的apply_async()方法,这是一种异步非阻塞的运行方法,即进程池的所有进程根据规则随时切换运行,比阻塞式的apply()方法要快。
调用join()之前必须先调用close(),只有关闭进程池,才能让里面的进程运行完毕。
import os,time,random
from multiprocessing import Pool
def run_fun(name):
start=time.time()
print('%s child process %s start'%(name,os.getpid()))
time.sleep(random.random()*3)
end=time.time()
print('%s child process takes %0.2f seconds'%(name,end-start))
if __name__=='__main__':
print('Parent process %s start'%os.getpid())
#参数n为进程池中可容纳的最大进程数。
p=Pool(4)
for i in range(5):
p.apply_async(run_fun,args=(i,))
print('waiting for all process finished')
#关闭进程池之后就不能继续添加新的Process了
p.close()
p.join()
print('All process finished')
Parent process 12380 start
waiting for all process finished
0 child process 16952 start
1 child process 17588 start
2 child process 5768 start
3 child process 8756 start
2 child process takes 1.48 seconds
4 child process 5768 start
0 child process takes 1.84 seconds
1 child process takes 1.86 seconds
4 child process takes 0.52 seconds
3 child process takes 2.59 seconds
All process finished
Process finished with exit code 0
进程之间通信
队列(queue)
multiprocessing模块里的Queue方法可以生成队列,一个进程将数据放到队列里,另一个进程从队列里把数据拿出来,这样就实现了进程之间的通信。
from multiprocessing import Queue,Process
import os
def write(q):
print('process %s are writing'%os.getpid())
#把数据放进队列中
for value in ['a','b','c']:
q.put(value)
def read(q):
print('process %s are reading'%os.getpid())
#不停地从队列中取数据
while True:
value=q.get()
print(value)
if __name__=='__main__':
q=Queue()
#定义两个进程实现通信
p=Process(target=write,args=(q,))
pp=Process(target=read,args=(q,))
p.start()
pp.start()
p.join()
#由于进程2中存在死循环,所以手动终止该进程
pp.terminate()
输出:
process 13944 are writing
process 7764 are reading
a
b
c
管道(pipe)
multiprocessing模块里的Pipe方法可以生成管道。管道会返回两个对象,作为管道的两个端口。
默认这两个端口是双开的,即Pip(duplex=True),即均可进可出,设为False则为一进一出。
from multiprocessing import Pipe,Process
import os
def write(p):
print('process %s are writing'%os.getpid())
#向管道里发送数据
p.send('Hello world')
def read(p):
print('process %s are reading'%os.getpid())
#不停地从管道里读取数据
while True:
print('I read:',p.recv())
if __name__=='__main__':
p=Pipe()
#分别给两个进程各分配一个端口
p1=Process(target=write,args=(p[0],))
p2=Process(target=read,args=(p[1],))
p1.start()
p2.start()
p1.join()
#进程1结束之后,往管道里发送数据,进程2依然会不停的读取
p[0].send('fuck')
#若2s内没有数据进入管道,则强制结束进程
p2.join(2)
print('All finished')
输出:
process 10084 are writing
process 2536 are reading
I read: Hello world
I read: fuck
All finished