Python 学习之进程与线程 「 上 」

阅读本文预计 8 分钟

进程与线程

进程:对于操作系统来说,一个任务就是一个进程(Process),比如打开一个浏览器(任务)就是启动一个浏览器进程。进程是系统中程序执行和资源分配的基本单位,每个进程都有自己的数据段、代码段和堆栈段。

线程:有些进程不止同时干一件事情,比如Word,它可以同时进行打字、拼写检查、打印等事情。在一个进程内部,要同时干多件事情,就需要同时运行多个“子任务”,我们把进程内的这些“子任务”称为线程(Thread),线程是最小的执行单元。

实现多任务原理

现代操作系统比如 Mac OS X,UNIX,Linux,Windows 等,都支持“多任务”。多任务指的是操作系统可以同时运行多个任务,比如同时在聊 QQ,听音乐,码字…

单核CPU实现多任务原理:CPU执行代码都是顺序执行的,操作系统轮流让各个任务交替执行,QQ 执行 2us 秒,切换到听音乐,听音乐执行 2us 秒,再切换到 word,执行 2us 秒…每个任务都是交替执行的,但是由于CPU的调度执行速度实在是太快了,我们感觉就像所有任务都在同时执行一样。

多核CPU实现多任务原理:真正的并行执行多任务只能在多核CPU上实现,但是,由于任务数量远远多于CPU的核心数量,所以,操作系统也会自动把很多任务轮流调度到每个核心上执行。

并行与并发

并发:看上去同时执行,任务数多于核心数 并行:真正同时执行,任务数小于等于核心数

多任务的实现方式:

  1. 多进程模式
  2. 多线程模式
  3. 协程模式
  4. 多进程+多线程模式

单进程(任务)现象

代码块
from time import sleep

def run():
    while 1:
        print("执行子进程")
        sleep(1)

if __name__ == "__main__":
    while 1:
        print("执行父进程")   # 父进程不结束,子进程无法开始
        sleep(1)

    run()  # 不会执行到 run 方法,只有上面的 while 循环结束才可以执行
运行结果

多进程(任务)现象

1.任务间互不干扰

启动进程执行多任务,各个任务间互不干扰。multiprocessing 库,跨平台版本的多进程模块,提供了一个 Process 类来代表一个进程对象

代码块
from multiprocessing import Process
from time import sleep
import os

def run1(num):
    while 1:
        # getpid() 获取当前进程 id 号,getppid() 获取当前进程的父进程 id 号
        print("执行子进程-%s" % num, os.getpid(), os.getppid())
        sleep(2)

def run2(num):
    while 1:
        print("执行子进程-%s" % num)
        sleep(3)

if __name__ == "__main__":
    while 1:
        print("启动父(主)进程", os.getpid())

        # 创建子进程    target:进程执行的任务, args 为元组类型
        p1 =Process(target=run1, args=("01",))
        p1.start()  # 启动进程

        p2 =Process(target=run2, args=("02",))
        p2.start()  # 启动进程

        while True:
            print("执行主进程")
            sleep(1)
运行结果

2. 任务间相互干扰

事实上同时执行的各个任务之间并不是没有关联的,而是需要相互通信和协调,有时,任务1必须暂停等待任务 2 完成后才能继续执行,有时,任务 3 和任务 4 又不能同时执行。所以,多进程和多线程的程序的复杂度要远远高于我们前面写的单进程、单线程的程序。

代码块
from multiprocessing import Process
from time import sleep

def run(num):
    print("子进程启动")
    sleep(2)
    print("子进程结束")

if __name__ == "__main__":
    print("父进程启动")

    # 创建多个子进程,此处以一个为例
    p = Process(target=run, args=("1",))
    p.start()

    # 未加入 join() 方法时,父进程的结束不影响子进程。为了让父进程等待所有的子进程结束后再继续执行父进程,需要添加一个 join() 方法,通常用于进程间的同步
    p.join()

    print("父进程结束")
运行结果

3.启动大量子进程

以进程池的方式来批量创建多个子进程,体现多任务的并发执行

代码块
from multiprocessing import Pool
import time, os, random

def run(num):
    print("子进程 %d 启动 -- %s " % (num, os.getpid()))
    start = time.time()
    time.sleep(random.choice([1, 2, 3]))
    end = time.time()
    print("子进程 %d 结束 -- %s -- 耗时 %.2f " % (num, os.getpid(), end-start))

if __name__ == "__main__":
    print("父进程启动")
    # 以进程池来创建多个进程,Pool 默认大小是 CPU 核心数,表示可以同时执行的进程数量
    p = Pool()
    for i in range(10):
        # 创建进程,放入进程池统一管理
        p.apply_async(run, args=(i, ))
    # 在调用 join() 之前必须先调用 close() ,调用 close() 之后就不能再继续添加新的进程了
    p.close()
    # 进程池对象调用 join() ,会等待进程池中所有的子进程结束完再去执行父进程
    p.join()

    print("父进程结束")
运行结果

4.1文件拷贝(单进程)

当拷贝的文件量很少时,单进程的方式比较快,因为多进程需要先创建进程花费时间;当拷贝的文件量很大很多时,多进程的优势就体现出来了。例子中拷贝的是大小为 446 M 的多张图片,多进程花费的时间较少。

代码块
import os,time

# 实现文件的拷贝,此处文件大小为 446 M
def copyFile(rPath, wPath):

    fr = open(rPath, "rb")
    fw = open(wPath, "wb")

    content = fr.read()
    fw.write(content)

    fr.close()
    fw.close()

path = r"C:\Users\Mark\Pictures\pic1\12"
toPath = r"C:\Users\Mark\Pictures\pic1\14"

# 读取 path 下的所有文件
filesList = os.listdir(path)

# 启动 for 循环处理每一个文件
start = time.time()
for fileName in filesList:
    copyFile(os.path.join(path, fileName), os.path.join(toPath, fileName))
end = time.time()

print("总耗时:%0.2f " % (end-start))  # 总耗时:19.03

4.2文件拷贝(多进程)

代码块
import os,time
from multiprocessing import Pool

# 实现文件的拷贝
def copyFile(rPath, wPath):
    fr = open(rPath, "rb")
    fw = open(wPath, "wb")

    content = fr.read()
    fw.write(content)

    fr.close()
    fw.close()

path = r"C:\Users\Mark\Pictures\pic1\12"
toPath = r"C:\Users\Mark\Pictures\pic1\14"

if __name__ == "__main__":
    # 读取 path 下的所有文件
    filesList = os.listdir(path)

    # 启动 for 循环处理每一个文件
    start = time.time()
    p = Pool()

    for fileName in filesList:
        p.apply_async(copyFile, args=(os.path.join(path,fileName),os.path.join(toPath,fileName)))

    p.close()
    p.join()
    end = time.time()
    print("总耗时:%0.2f " % (end - start))  # 总耗时:15.76

5.封装进程对象

一般而言,父进程主要是负责调度子进程的执行,不具体负责某项任务,所以需要进行封装进程对象。将实现各个功能、任务的子线程封装起来,父线程只需负责调用,提高了代码的逻辑与整洁性。

代码块
父进程调用代码
from childProcess import childProcess

if __name__ == "__main__":

    print("父进程启动")

    # 创建子进程
    p = childProcess("01")
    # 自动调用 p 进程对象的 run 方法
    p.start()
    p.join()

    print("父进程结束")
子进程实现功能代码
from multiprocessing import Process
import os,time

class childProcess(Process):
    def __init__(self, num):
        Process.__init__(self)
        self.num = num

    def run(self):
        print("子进程( %s -- %s )启动" % (self.num, os.getpid()))
        # 子进程的功能
        time.sleep(3)
        print("子进程( %s -- %s )结束" % (self.num, os.getpid()))
运行结果

原文发布于微信公众号 - Python梦工厂(AzMark950831)

原文发表时间:2018-08-06

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏开源优测

python selenium - 利用excel实现参数化

前言 在进行软件测试或设计自动化测试框架时,一个比可避免的过程就是: 参数化,在利用python进行自动化测试开发时,通常会使用excel来做数据管理,利用xl...

2937
来自专栏我是攻城师

关于线程死锁问题

死锁是多线程编程里面非常常见的一个问题,作为一个中高级开发者是必须掌握的内容,今天我们来学习一下死锁相关的知识。

1256
来自专栏开源优测

python selenium - 利用excel实现参数化

前言 在进行软件测试或设计自动化测试框架时,一个比可避免的过程就是: 参数化,在利用python进行自动化测试开发时,通常会使用excel来做数据管理,利用xl...

3808
来自专栏开源优测

python selenium - 利用excel实现参数化

前言 在进行软件测试或设计自动化测试框架时,一个比可避免的过程就是: 参数化,在利用python进行自动化测试开发时,通常会使用excel来做数据管理,利用xl...

2998
来自专栏小樱的经验随笔

【Python Learning第一篇】Linux命令学习及Vim命令的使用

学了两天,终于把基本命令学完了,掌握以后可以当半个程序员了♪(^∇^*) 此文是一篇备忘录或者查询笔记,如果哪位大佬看上了并且非常嫌弃的话,还请大佬不吝赐教,多...

38710
来自专栏编程微刊

微信小程序从零开始开发步骤(七)引入外部js 文件

2544
来自专栏coder修行路

Python并发编程协程(Coroutine)之Gevent

Gevent官网文档地址:http://www.gevent.org/contents.html 基本概念 我们通常所说的协程Coroutine其实是corpo...

42810
来自专栏深度学习之tensorflow实战篇

交互式使用 R题(shell)

交互式使用 R 交互式shell是一种很方便的环境,可以进行各种尝试,随时调整过程。与Python、Ruby等语言一样,R也提供了shell环境。本文开始的例子...

3105
来自专栏Java工程师日常干货

【SpringBoot专题】多环境配置及swagger前言多环境配置分析swagger

在上一篇博客《【SpringBoot专题】快速体验 》中已经带领大家初步了解了SpringBoot,本篇博客将为大家介绍多环境配置、swagger等相关内容。

1174
来自专栏IT杂记

关于JVM CPU资源占用过高的问题排查

一、背景:     先执行一个java程序里面开了两个线程分别都在while循环做打印操作。 # java -cp ./test-threads.jar...

2699

扫码关注云+社区

领取腾讯云代金券