关于python:Python-多线程多进程

46次阅读

共计 4341 个字符,预计需要花费 11 分钟才能阅读完成。

前提

我是参考 Github Python 100 天的文章写的,再联合本人的小练习,总结
最近在面大厂,发现许多大厂都会问 Python 的多线程、多过程,所以我觉得很有必要总结学习下。

什么是过程

操作系统中执行的一个程序,相似微信、QQ,每个程序都是一个过程

概念

  • 它是 CPU 最小资源分配单元
  • 操作系统会给过程分配内存空间,每个过程都会有本人的地址空间、数据栈以及其余用于跟踪过程执行的辅助数据
  • 操作系统治理所有过程的执行,为它们正当的分配资源

fork、spawn

  • 过程能够通过 fork、spawn 的形式来创立新的过程来执行其余工作
  • 不过新的过程有本人独立的内存空间、数据栈
  • 因而不同过程间须要通过通信机制(IPC)来实现数据共享

常见通信机制

  • 管道
  • 信号
  • 套接字
  • 共享内存区

什么是线程

  • 过程中能够领有多个并发的执行线索
  • 它是 CPU 最小调度的执行单元

特点

  • 同一个过程下的线程共享雷同的上下文
  • 绝对于过程来说,线程间的信息共享和通信更加容易

单核 CPU 零碎注意事项

  • 真正的并发是不可能的
  • 因为在某个时刻,CPU 只能运行惟一的一个线程
  • 多个线程共享了 CPU 的执行工夫

多线程的益处

  • 晋升程序的性能和改善用户体验
  • 明天日常应用的软件简直都用到了多线程

多线程的害处

  • 站在其余过程的角度,多线程的程序对其余程序并不敌对,因为它占用了更多的 CPU 执行工夫,导致 - – 其余程序无奈取得足够的 CPU 执行工夫
  • 编写和调试多线程的程序对开发者要求较高

Python 实现并发编程的形式

  • 多过程
  • 多线程
  • 多过程 + 多线程

Python 中的多过程

Linux 下的 fork 函数

  • Linux 操作系统上提供了 fork() 零碎调用来创立过程
  • 调用 fork() 函数的是父过程
  • 创立的是子过程
  • 子过程是父过程的拷贝
  • 但子过程有本人的 PID
  • fork() 函数十分非凡,它会返回两次,父过程中调用 fork() 会返回子过程的 PID,子过程中调用 fork() 失去的都是 0

Python 提供的 fork 函数

os 模块提供了 fork()

Window 下没有 fork() 的调用
实现跨平台的多过程变成,能够应用 multiprocessing 模块的 Process 类来创立子过程
还提供了更高级的封装,例如批量启动过程的过程池 pool、用于过程间同喜你的队列 Queue 和管道 Pipe

应用多过程和不应用多过程的区别(写代码)

不应用多过程

from random import randint
from time import time, sleep

def download_task(filename):
    print('开始下载 %s...' % filename)
    time_to_download = randint(5, 10)
    sleep(time_to_download)
    print('%s 下载实现! 消耗了 %d 秒' % (filename, time_to_download))


def main():
    start = time()
    download_task('Python 从入门到住院.pdf')
    download_task('Peking Hot.avi')
    end = time()
    print('总共消耗了 %.2f 秒.' % (end - start))

if __name__ == '__main__':
    main()
 

执行后果

开始下载 Python 从入门到住院.pdf...
Python 从入门到住院.pdf 下载实现! 消耗了 10 秒
开始下载 Peking Hot.avi...
Peking Hot.avi 下载实现! 消耗了 9 秒
总共消耗了 19.02 秒.

能够看到须要先等第一个文件下载完能力下载第二个文件,效率很低

应用多过程

from random import randint
from time import time, sleep
from multiprocessing import Process

def download_task(filename):
    print('开始下载 %s...' % filename)
    time_to_download = randint(5, 10)
    sleep(time_to_download)
    print('%s 下载实现! 消耗了 %d 秒' % (filename, time_to_download))

def main2():
    start = time()
    p1 = Process(target=download_task,args=("Python 从入门到住院.pdf",))
    p1.start()
    p2 = Process(target=download_task, args=("Peking Hot.avi",))
    p2.start()
    p1.join()
    p2.join()
    end = time()
    print('总共消耗了 %.2f 秒.' % (end - start))


if __name__ == '__main__':
    main2()
 

执行后果

开始下载 Python 从入门到住院.pdf...
开始下载 Peking Hot.avi...
Python 从入门到住院.pdf 下载实现! 消耗了 6 秒
Peking Hot.avi 下载实现! 消耗了 10 秒
总共消耗了 10.17 秒.

两个工作同时执行,总耗时不再是两个工作的工夫总和

知识点

  • Process:通过 Process 类创立过程对象
  • target:通过 target 参数传入一个函数名来示意过程启动后要执行的代码
  • args:是一个元组,代表传递给函数的参数列表
  • start:Process 的 start() 办法来启动过程
  • join:Process 的 join() 办法示意期待过程执行完结,才会往下执行

Python 中的多线程

前言

举荐 threading 模块来实现多线程编程,它提供了更好的面向对象封装

多线程的实现形式

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
__title__  = 
__Time__   = 2021/3/19 18:17
__Author__ = 小菠萝测试笔记
__Blog__   = https://www.cnblogs.com/poloyy/
"""

from random import randint
from threading import Thread
from time import time, sleep

def download_task(filename):
    print('开始下载 %s...' % filename)
    time_to_download = randint(5, 10)
    sleep(time_to_download)
    print('%s 下载实现! 消耗了 %d 秒' % (filename, time_to_download))


def main3():
    start = time()
    p1 = Thread(target=download_task,args=("Python 从入门到住院.pdf",))
    p1.start()
    p2 = Process(target=download_task, args=("Peking Hot.avi",))
    p2.start()
    p1.join()
    p2.join()
    end = time()
    print('总共消耗了 %.2f 秒.' % (end - start))


if __name__ == '__main__':
    main3()

执行后果

开始下载 Python 从入门到住院.pdf...
开始下载 Peking Hot.avi...
Peking Hot.avi 下载实现! 消耗了 6 秒
Python 从入门到住院.pdf 下载实现! 消耗了 8 秒
总共消耗了 8.01 秒.

一样执行效率高很多

自定义线程类

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
__title__  = 
__Time__   = 2021/3/19 18:17
__Author__ = 小菠萝测试笔记
__Blog__   = https://www.cnblogs.com/poloyy/
"""

from random import randint
from threading import Thread
from time import time, sleep

class downLoadTask(Thread):
    def __init__(self,filename):
        super().__init__()
        self.filename = filename

    def run(self) -> None:
        print('开始下载 %s...' % self.filename)
        time_to_download = randint(5, 10)
        sleep(time_to_download)
        print('%s 下载实现! 消耗了 %d 秒' % (self.filename, time_to_download))

def main3():
    start = time()
    p1 = downLoadTask("Python 从入门到住院.pdf")
    p2 = downLoadTask("Peking Hot.avi")
    p1.start()
    p2.start()
    p1.join()
    p2.join()
    end = time()
    print('总共消耗了 %.2f 秒.' % (end - start))

if __name__ == '__main__':
    main3()
 

执行后果

开始下载 Python 从入门到住院.pdf...
开始下载 Peking Hot.avi...
Peking Hot.avi 下载实现! 消耗了 6 秒
Python 从入门到住院.pdf 下载实现! 消耗了 9 秒
总共消耗了 9.00 秒.

也是一样的高效运行

重点常识:start 和 run 办法的区别

比拟点 start run
作用 启动线程,获取 CPU 工夫片 运行线程指定的代码块
线程状态 可运行状态 运行状态
调用次数 一个线程只能调用一次 能够反复调用
运行线程 创立了一个子线程,线程名是本人命名的 在主线程中调用了一个一般函数
留神点 想用多线程,必须调用 start()

Python 中的协程

什么是协程

Python 中,单线程 + 异步 I/O 的编程模型

协程的劣势

  • 极高的执行效率
  • 子程序切换不是线程切换,而是由程序自身管制,没有线程切换的开销
  • 不须要多线程的所机制,只有一个线程,所以不存在同时写变量抵触,在协程中管制共享资源不必加锁,只须要判断状态就好了,所以执行效率比多线程高很多

重点

要充分利用 CPU 的多核个性,应该应用多过程 + 协程的形式

待更新

EOF

本文作者:小菠萝测试笔记
本文原文链接:https://www.cnblogs.com/poloy…
版权申明:本博客所有文章除特地申明外,均采纳 BY-NC-SA 许可协定。转载请注明出处!

本文转自:SDK 社区(sdk.cn)是一个中立的社区,这里有多样的前端常识,有丰盛的 api,有爱学习的人工智能开发者,有有趣风趣的开发者带你学 python,还有将来炽热的鸿蒙,当各种元素组合在一起,让咱们一起脑洞大开独特打造业余、好玩、有价值的开发者社区,帮忙开发者实现自我价值!

正文完
 0