进程和线程
1、进程和线程的概念
进程
是正在执行的程序,是多任务操作系统中执行任务的基本单元,是包含了程序指令和相关资源的集合,是程序执行时的一个实例,在程序运行时创建的。
线程
线程是进程的执行单元。在大多数程序中,可能只有一个主线程,但是为了提高效率,有些程序会采用多线程,在系统中所有线程看起来都是同时执行的。
线程和进程的对比
进程是重量级的,具体包括进程映像的结构,执行细节以及进程间切换的方法。在进程中,需要处理的问题包括进程间通信,临界区管理和进程调度等,这些特性使得生成一个进程的开销是比较大的。
线程是轻便的,线程之间共享很多资源,容易进行通信,生成一个线程的开销很小,但是线程会出现死锁,数据同步和实现复杂的问题
并发编程解决方案
-
启动多个进程,每个进程只有一个线程,但多个进程可以一块执行多个任务
-
启动一个进程,在一个进程中启动多个线程,这样,多个线程也可以一起执行多个任务
-
启动多个进程,每个进程再启动多个线程,这样执行的任务更多,但是这种方法更加复杂,很少采用
python在多进程处理CPU密集型程序(各种循环处理,计数等等)比较友好,在多线程处理IO密集型代码(文件处理,网络爬虫等)比较友好
2、进程的开发
python标准库中相关模块
模块 | 介绍 |
---|---|
subprocess | python基本库中多线程编程相关模块,适用于外部进程交互,调用外部进程 |
multiprocessing | 核心机制是fork,重开一个进程,首先会把父进程的代码copy重载一遍 |
threading | python基本库多线程管理相关模块 |
subprocess模块
run 方法
subprocess.run(args, *, stdin=None, input=None, stdout=None, stderr=None, capture_output=False, shell=False, cwd=None, timeout=None, check=False, encoding=None, errors=None, text=None, env=None, universal_newlines=None)
- args:表示要执行的命令。必须是一个字符串,字符串参数列表。
- stdin、stdout 和 stderr:子进程的标准输入、输出和错误。其值可以是 subprocess.PIPE、subprocess.DEVNULL、一个已经存在的文件描述符、已经打开的文件对象或者 None。subprocess.PIPE 表示为子进程创建新的管道。subprocess.DEVNULL 表示使用 os.devnull。默认使用的是None,表示什么都不做。另外,stderr 可以合并到 stdout 里一起输出。
- timeout:设置命令超时时间。如果命令执行时间超时,子进程将被杀死,并弹出 TimeoutExpired 异常。
- check:如果该参数设置为 True,并且进程退出状态码不是 0,则弹 出 CalledProcessError 异常。
- encoding: 如果指定了该参数,则 stdin、stdout 和 stderr 可以接收字符串数据,并以该编码方式编码。否则只接收 bytes 类型的数据。
- shell:如果该参数为 True,将通过操作系统的 shell 执行指定的命令。
import subprocess
# 调用外部命令(如window的dir,ipconfig,linux等命令等)
runcmd=subprocess.run('dir D:',encoding='utf-8',shell=True)
# 显示d盘下所有的目录
print(runcmd)
run方法可用文件句柄形式传参
import subprocess
f=open(r'd:\\sxt2.txt','w+')
run_cmd=subprocess.run('python',stdin=f,stdout=subprocess.PIPE,shell=True)
#调用python程序命令
run_cmd.stdin='print("你好")'
f.write(run_cmd.stdin)
f.close()
print(run_cmd.stdout.decode('utf-8'))
优点:可以通过执行python程序,对stdin传参,通过stdout输出
缺点:需要提前指定文件,并不方便,对进程的使用并不友好
Popen 方法
class(类) subprocess.Popen(args, bufsize=-1, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=True, shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0,restore_signals=True,start_new_session=False, pass_fds=(),*, encoding=None, errors=None)
常用参数
- args:shell命令,可以是字符串或者序列类型,调用的程序(如:list,元组)
- bufsize:缓冲区大小。当创建标准流的管道对象时使用,默认-1。
0:不使用缓冲区
1:表示行缓冲,仅当universal_newlines=True时可用,也就是文本模式
正数:表示缓冲区大小
负数:表示使用系统默认的缓冲区大小。 - stdin, stdout, stderr:分别表示程序的标准输入、输出、错误句柄
- shell:如果该参数为 True,将通过操作系统的 shell 执行指定的命令。
import subprocess
popen=subprocess.Popen('python',stdin=subprocess.PIPE,stdout=subprocess.PIPE,shell=True)
# 调用python这个程序命令,从标准输入里面写入,在标准输出输出
popen.stdin.write('print("hello")\n'.encode('utf-8'))
popen.stdin.write('import math\n'.encode('utf-8'))
popen.stdin.write("print(math.sqrt(4))\n".encode('utf-8'))
popen.stdin.close()
result=popen.stdout.read().decode('utf-8')
popen.stdout.close()
print(result)
Popen 对象方法
poll()
: 检查进程是否终止,如果终止返回 returncode,否则返回 None。wait(timeout):
等待子进程终止,设定timeout,超过这个时间就自动结束。communicate(input,timeout)
: 和子进程交互,发送和读取数据。terminate()
: 停止子进程,也就是发送SIGTERM信号到子进程。kill()
: 杀死子进程。发送 SIGKILL 信号到子进程
multprocessing模块
class(类) multiprocessing.Process(group=None, target=None, name=None, args=(), kwargs={
}, daemon=None)
常用参数
-
group:一般都为None
-
target:run()方法调用的函数
-
name:表示进程调用的名字
-
args:表示目标调用的元组
-
kwargs:表示目标调用的字典
-
daemon:表示守护线程,当父进程结束后,子进程会强制结束
常用方法
run()
:表示进程活动的方法。你可以在子类中重载此方法。标准 run方法用传递给对象构造函数的可调用对象作为目标参数(如果有),分别从 args 和 kwargs 参数中获取顺序和关键字参数。start()
:启动进程活动。这个方法每个进程对象最多只能调用一次。它会将对象的 放法安排在一个单独的进程中调用。join
([timeout])如果可选参数 timeout 是 None (默认值),则该方法将阻塞,直到调用 join方法的进程终止。如果 timeout 是一个正数,它最多会阻塞 timeout 秒。请注意,如果进程终止或方法超时,则该方法返回None
。检查进程的 exitcode 方法的进程终止。如果 timeout 是一个正数,它最多会阻塞 timeout 秒。请注意,如果进程终止或方法超时,则该方法返回None
。close
()关闭Process
对象,释放与之关联的所有资源。
创建子进程(函数)
import multiprocessing
import os
import time
def fun(name):
print("该进程的序号",name)
print("父进程的id",os.getpid())# getpid获取当前进程的id地址
print("子进程的id",os.getpid())
print()
time.sleep(3) # 停格三秒
if __name__ == '__main__':
# 创建多个子进程来调用函数
start=time.time()
for i in range(10):
p=multiprocessing.Process(target=fun,args=('进程%d'%i,)) # 创建一个子进程
p.start() # 开启一个子进程
print("进程完成")
# 进程之间并不会出现堵塞的现象,父进程会跟着子进程所有的代码结束才会完全停止
end=time.time()
print(end