这里杂乱的介绍window系统下 多进程相关库multiprocess 进程 线程 并发 并行概念 初步有个大致的印象 IDE:Jetbrain pycharm 2019
python基础系列 正在持续更新中:)
文章目录
- multiprocess定义 + 更改快捷键 + 查询字段定义
- args kwargs
- multiprocess使用
- run start terminate
- 进程 线程 多任务
- 任务调度 并发 并行
multiprocess定义 + 更改快捷键 + 查询字段定义
首先我们得找到其定义(definition),选中一个词然后在View-Quick Definition就能够实现
建议可以更改快捷键为Ctrl+D,如下图:File-Settings:
接下来 Ctrl+D 查询multiprocess 似乎因为版本关系,原来直接在multiprocess能看到的class process定义没了,而是再找Baseprocess:
class BaseProcess:
name: str
daemon: bool
authkey: bytes
def __init__(
self,
group: None = ...,
target: Optional[Callable[..., Any]] = ...,
name: Optional[str] = ...,
args: Tuple[Any, ...] = ...,
kwargs: Mapping[str, Any] = ...,
*,
daemon: Optional[bool] = ...,
) -> None: ...
def run(self) -> None: ...
def start(self) -> None: ...
def terminate(self) -> None: ...
可见process有几个属性:
名称 | 说明 | 中文 |
target | Callable | 可被调用的目标 |
name | str | 名字 字符串形式 |
args | Tuple | 元祖形式的参数 |
kwargs | Mapping str | 键值对形式的参数 |
还有几个方法 run(运行) start(开启) terminate(终止)
下面我们先理解一下args kwargs
args kwargs
*args = arguments 参数(形参)的意思
**kwargs = key word arguments 所以 是“键值参数”?
No, 键值对参数,还记得字典dict的键值对嘛,没错,这个kwargs只接受键值对传入,其他的事不关己高高挂起,同样args也不会越俎代庖,也只接受 多个 普通的单值参数比如字符串,数字,etc
来试试下面的例子:
def ryan_test(arg,*args,**kwargs):
print arg
print args
print kwargs
Ryan_test(1,2,3,d='4',e=5)
一个萝卜一个坑,形参arg就对应第一个传入参数1,
而键值对(keywords) d=‘4’,e=5 被**kwargs承包了,
意味着只有*args能够收拾剩下的烂摊子——数字 2,3,
注意是args 带s
意味着复数,意思多个单值参数
输出结果:
1
(2, 3)
{'e': 5, 'd': '4'}
我们再来看个案例:python的dict类构造函数是怎么工作的:myDICT = dict(a=1,b=2,c=3)
这个能够生成值为{a=1,b=2,c=3}的一个字典
其实就可以用**kwargs实现:
def ryan_dict(**kwargs):
return kwargs
print ryan_dict(a=1,b=2,c=3) == {'a':1, 'b':2, 'c':3}
你可以封装成一个类,我比较懒就不管了:)
multiprocess使用
我们利用multiprocess的Process对象 创建 进程
创建进程的类
Process([group [, target [, name [, args [, kwargs]]]]]),由该类实例化得到的对象,表示一个子进程中的任务(尚未启动)
创建子进程需要传入参数 比如我就Process(target=task_1, name="任务1",args=(1,'Ryan',))
意思 执行任务 task_1函数,子进程名字为“任务1”,传入参数(1,‘Ryan’)。
我们直接来看一个栗子 就理解了:
#-*- utf-8 -*-
from time import sleep
import os
from multiprocessing import Process
def task_1(second, name):
while True:
sleep(second)
print("TASK " + name + "-- "+str(os.getpid()),"--",str(os.getppid()))
def task_2(second, name):
while True:
sleep(second)
print("TASK " + name + "-- "+str(os.getpid()),"--",str(os.getppid()))
if __name__ == '__main__':
print(os.getpid())
p1 = Process(target=task_1, name="任务1",args=(1,'Ryan',))
p1.start()
p2 = Process(target=task_2, name="任务2",args=(2,'sst',))
p2.start()
这里解释一下 task_1 task_2 就是子进程,因为用了start()
函数,所以是作为子进程(ChildProcess) 我们利用getpid()
函数获得进程的识别码ID PID = process ID
可以看到
Ryan 子进程 PID = 9796
sst 子进程PID = 2412
那1816就是父进程(parent process) 用getppid()
也就是:get parent pid,从而 获得其pid
打开任务管理器 taskmgr 可见我们两个子进程 一个父进程
run start terminate
start()
启动我们Process创建的实例——一个进程,然后会默认调用run()
,run()只负责执行属性target
中的函数,其他的不管,意味着使用它不能实现进程,以及单核并发,或者多核并行逻辑
总之 使用start()
就对了terminate()
就是用于终止子进程的,类似的函数还有kill()
进程 线程 多任务
进程可以说是线程(Threadings, thread有织毛衣的线的意思)的爸爸 集合。进程,就像一个包含很多线程的大家庭。线程就是兄弟姐妹,共有一个进程爸爸。兄弟姐妹间聊天比较畅通无阻,所以线程间是可以直接信息交流的,甚至可以创建、撤销新的进程,而且共享系统资源(共享一个家),自己只能拥有运行必须的最小资源(比如自己的一个小房间),所以线程占用资源很少。当然,每家每户都有一块地,这就是系统os为进程开辟的内存空间,所以每开一个进程,就要求系统分配一份空间。
家庭虽然是公用的,但是总有公共部分,譬如厕所,一次可能只能有一个人在里面使用。线程也是如此,常常是并发执行concurrent,所以先后不重要(代码放的位置先后),因为是轮询的,总会轮到你。
但是进程间就是一个个分立的家庭了,资源不共享(你家和别家,不在一个屋檐下,资源不太可能共享),另外,信息共享也不容易,所以不是直接信息传递,往往通过pipe等间接传信息的
任务调度 并发 并行
CPU的任务调度( task dispatcher) 这里记一下dispatch这个单词:patch 斑点,星星点点就好像是撒出去一样,派发出去一样,所以dispatch 派发 派遣。CPU派遣调度人,就是dispatcher
那么假设我们只有单核的CPU,我们需要实现多任务同时执行,然而并不可能,因为只有一个处理器CPU,于是就可以花式调度任务——快速切换,比如10us执行任务1,10us执行task2,我们肉眼看来就好像是同时进行的。
那么 单核CPU进行这项花式操作,就是并发(concurrenty, i.e con-current-y)
如果多核呢?目前撑死也就16核,任务数量还是远多于核的数量,但比起单核还是轻松一些——比如有4核,那么4个任务(task1,2,3,4)是真正 同时进行的
核1 10us执行task1,10us执行task5
核2 10us执行task2,10us执行task6
核3 10us执行task3,10us执行task7
核4 10us执行task4,10us执行task8
这样的称为 并行(Parallelism,i.e Parallel-ism)