Python札记50_进程和线程1

现在的操作系统不管是Mac OS X ,UNIX,Linux或者Windows都是支持“多任务”的操作系统。多任务指的是同时运行多个任务,比如此时笔者正在用浏览器上网,也在输入文字,同时QQ还在运行等。

  • CPU中执行代码是按照顺序执行的
  • 单核CPU:操作系统让每个任务交替执行;CPU运行速度快,用户感觉就像是同时运行
  • 多核CPU:真正意义上同时执行多个任务。任务数量远多于CPU核数,系统让每个任务轮流到核心上执行

在此感谢廖雪峰老师的教程:进程和线程


进程process

对于操作系统,一个任务就是一个进程。有些进程的内部不是只做一件事情,比如开启word:打字、拼写检查、打印等同时进行。进程内部同时进行多个“子任务”,进程内部的“子任务”就是线程。进程特点:

  • 一个进程至少有一个线程
  • 一个进程中可以有多个线程,多个线程同时进行

线程Thread

一个进程内部有多个“子任务”,这些子任务就是线程。

  • 多线程的执行由操作系统在多个线程之间快速切换
  • 真正的多线程由多核CPU才能实现
  • Python程序就是单任务的进程,也就是只有一个线程。

多个任务同时执行怎么实现?
1.多进程模式
2.多线程模式
3.多进程+多线程模式


多进程multiprocessing

关于fork()
Linux中提供了fork()调用。特殊之处在于:普通函数被调用只返回一次;调用一次,返回两次。操作系统将当前进程(父进程)复制一份(子进程),分别在父进程和子进程内进行返回。

  • 子进程返回的是0
  • 父进程返回的是子进程的ID
  • 一个父进程可以fork多个子进程,每个父进程记下每个子进程的ID
  • 子进程调用getppid()就可以得到父进程的ID
    • os.getpid():获取子进程id
    • os.getppid():获取父进程id(parent pid)

如何在Python中创建子进程?

Python中的os模块封装了常用的系统调用,在程序中轻松地调用创建子进程:

import os   # 导入模块;os模块实现系统进程的封装
print("Process {} start...".format(os.getpid()))   # 查询父进程
pid  = os.fork()   # 调用函数fork
if pid == 0:  # 子进程的pid是0
    print("I am child {0} and my parent is {1}".format(os.getpid(), os.getppid()))
else:
    print("I {0} just started a child  process{1}".format(os.getpid(), pid))

forkwindows中没有,只有在Linux或者Mac系统中存在。

image.png

总结:

  • 有了fork调用,进程在接收到新的任务会复制出新的子进程来处理任务,常见的apache服务器就是通过这样的方式,fork出子进程来处理子进程。
  • fork()调用执行两次;fork()只能在LinuxUnix上执行
  • Windows上 无法直接执行,只能调用multiprocessing模块

根据上面的描述,Windows系统是不提供fork功能的,如何在Windows上怎么实现多进程?使用multiprocessingmultiprocessing模块是跨平台版本的多进程模块。模块中提供了一个Process类来代表一个进程对象。

  1 import os
  2 from multiprocessing import Process
  3 
  4 # 子进程执行的代码
  5 def run_proc(name):
  6     print(“Run child process {0} {1}”.format(name, os.getpid()))
  7 
  8 if __name__ == "__main__":
  9     print("Parent process {0}".format(os.getppid()))   # 打印当前父进程
 10     p = Process(target=run_proc, args=('test',))   # 通过类创建实例p
 11     print("Child process will start")   # 提示:子进程开启
 12     p.start()  # 调用实例的开启方法
 13     p.join()   # 用于进程间的同步
 14     print("Child process end")                                                                                                                                                                                        
image.png
  • 创建子进程需要传入一个执行函数和函数的参数
  • 创建Process实例,利用start()方法启动
    -jion()方法是子进程结束之后再继续往下运行,通常用于进程间的同步

批量创建子进程

如果要启动大量的子进程,使用进程池Pool的方法来实现

  1 from multiprocessing import Pool   # 导入进程池的类
  2 import os,time,random   # 导入三种模块
  3 
  4 def long_time_task(name):   # 定义任务执行函数
  5     print("Run task {} {}".format(name, os.getpid()))   # 获取当前子进程信息
  6     start = time.time()   # 任务执行的开始时间,时间戳形式
  7     time.sleep(random.random() * 3)   # 随机sleep时间
  8     end = time.time()   # 任务结束时间
  9     print("Task {} runs {:.2f} seconds".format(name, (end - start)))
 10 
 11 if __name__ == "__main__":
 12     print("Parent process {}".format(os.getppid()))  # 获取当前父进程信息
 13     p = Pool(4)   # 通过类创建实例p
 14     for i in range(5):
 15         p.apply_async(long_time_task, args=(i, ))  #  通过实例的apply_async()方法创建进程;一个函数,一个是函数执行的参数
 16     print("Waiting for all subprocess done...")
 17     p.close()  # close必须在join的前面
 18     p.join()
 19     print("All subprocess done")

执行结果:

Parent process 27501
Waiting for all subprocess done...
Run task 0 18868
Run task 1 18869
Run task 2 18870
Run task 3 18871
Task 2 runs 0.86 seconds
Run task 4 18870
Task 0 runs 0.91 seconds
Task 1 runs 1.25 seconds
Task 4 runs 1.08 seconds
Task 3 runs 2.99 seconds
All subprocess done

解释

  • join()方法会等待所有的子进程执行完毕
  • close()方法必须要在join()之前;close之后就不能调用新的Process
  • Pool的大小默认是CPU的核数

子进程

有时候子进程并不是自身,而是一个外部的进程。可以通过subprocess模块来控制子进程的输入和输出。
例如下面的代码等同于运行命令'nslookup www.python.org'

import subprocess    # 导入模块

print('$ nslookup www.python.org')   
r = subprocess.call(['nslookup', 'www.python.org'])   # 调用模块的call方法
print('Exit code:', r)
image.png

通过communicate()控制子进程继续输入

进程间通信

各种进程间肯定是需要通信的。Pythonmultiprocessing模块包装了底层的机制,提供了Queue、Pipes等多种方式来交换数据。
以Queue为例,在父进程中创建两个子进程,一个往Queue中读数据,一个写数据:

from multiprocessing import Process, Queue
import os, time, random

# 写入数据
def write(q):
    print("Process to write: {}".format(os.getpid()))   # d输出子进程pid
    for vlaue in ['A', 'B', 'C']:
        print("Put {} to queue...".format(value))
        q.put(value)
        time.sleep(random.random())
        
# 读数据
def read(q):
    print("Process to read: {}".format(os.getpid()))
    while True:
        value = q.get(True)
        print("Get {} from queue".format(value))
        
if __name__ == '__main__':
    q = Queue()   # 创建父进程,并且传给各个子进程
    pw = Process(target=write, args=(q,))  # 写子进程
    pr = Process(target=read, args=(q,))   # 读子进程
    pw.start()  # 开启两个子进程
    pr.start()
    pw.join() # 等待pw结束
    pr.terminate()  # 强行终止pr进程,因为while条件是True,死循环强行终止
  • Unix/Linux下,可以使用fork()调用实现多进程。

  • 要实现跨平台的多进程,可以使用multiprocessing模块。

  • 进程间通信是通过Queue、Pipes等实现的。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,402评论 6 499
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,377评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,483评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,165评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,176评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,146评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,032评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,896评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,311评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,536评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,696评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,413评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,008评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,815评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,698评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,592评论 2 353