胖蔡说技术
随便扯扯

Python中的进程与线程

任何后台语言的开发,对于并发编程的追求一直未曾改变,随着现代化的计算机的计算能力的提高,计算机的CPU也由单核进入多核时代,这也导致了我们更加深入的探索程序并发执行的必要性。并发可以让我们的计算机在极小的代价下同时执行多个任务,极大的减轻我们服务器的响应时间、提高用户的体验。而并发编程的核心就是多进程、多线程,而本文就是主要来了解Python开发中的进程、线程。

进程

对一个操作系统而言,进程是其执行程序的最小单元。系统通过进程来为程序分配:存储空间、地址空间、数据栈以及其他用于跟踪进程执行的辅助数据,操作系统管理所有进程的执行,为它们合理的分配资源。进程可通过fork或者是spawn方式创建新的进程来执行其他任务,新创建的进程也同样独自享有系统分配的一切资源,所以进程和进程之前的通信就不可和进程内的通信方式一样了,这里就有了进程间通信机制(IPC,Inter-Process Communication)来实现数据共享,Python中其具体的实现方式如下:

  • 管道Pipe:通信方式效率低,不适合进程间频繁地交换数据。
  • 消息队列:消息队列不适合比较大数据的传输。消息队列通信过程中,存在用户态与内核态之间的数据拷贝开销。
  • Socket套接字:Socket 通信不仅可以跨网络与不同主机的进程间通信,还可以在同主机上进程间通信。
  • 信号量:信号量其实是一个整型的计数器,主要用于实现进程间的互斥与同步,而不是用于缓存进程间通信的数据。
  • 信号:信号是进程间通信机制中唯一的异步通信机制,因为可以在任何时候发送信号给某一进程,一旦有信号产生,我们就有下面这几种,用户进程对信号的处理方式。
  • 共享内存:共享内存的机制,就是拿出一块虚拟地址空间来,映射到相同的物理内存中。

如下介绍一个使用示例来对比先单进程和多进程的区别:

1. 单进程实现

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下载完成! 耗费了6秒
开始下载Peking Hot.avi...
Peking Hot.avi下载完成! 耗费了7秒
总共耗费了13.01秒.

2. 多进程实现

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


def download_task(filename):
    print('启动下载进程,进程号[%d].' % getpid())
    print('开始下载%s...' % filename)
    time_to_download = randint(5, 10)
    sleep(time_to_download)
    print('%s下载完成! 耗费了%d秒' % (filename, time_to_download))


def main():
    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__':
    main()

# 运行结果
启动下载进程,进程号[1530].
开始下载Python从入门到住院.pdf...
启动下载进程,进程号[1531].
开始下载Peking Hot.avi...
Peking Hot.avi下载完成! 耗费了7秒
Python从入门到住院.pdf下载完成! 耗费了10秒
总共耗费了10.01秒.

线程

线程不同于进程,线程是进程中的最小执行单元,一个线程是一个execution context(执行上下文),即一个CPU执行时所需要的一串指令。Python中为了实现多线程的功能,给我们提供了两个模块:

  • thread:较为低级,用于比较底层的开发,应用级别使用不多
  • threading:对多线程编程提供了更好的面向对象的封装

一般推荐使用threading来作为python实现多线程的实现模块,threading中也提供了两种方式来实现多线程,一种是直接使用threading.Thread(),还有一种是通过继承threading.Thread来自定义线程类,重写run方法,如下使用threading来重新实现上述示例:

一、直接使用

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


def download(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()
    t1 = Thread(target=download, args=('Python从入门到住院.pdf',))
    t1.start()
    t2 = Thread(target=download, args=('Peking Hot.avi',))
    t2.start()
    t1.join()
    t2.join()
    end = time()
    print('总共耗费了%.3f秒' % (end - start))


if __name__ == '__main__':
    main()

二、继承Thread实现

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):
        print('开始下载%s...' % self._filename)
        time_to_download = randint(5, 10)
        sleep(time_to_download)
        print('%s下载完成! 耗费了%d秒' % (self._filename, time_to_download))


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


if __name__ == '__main__':
    main()
赞(0) 打赏
转载请附上原文出处链接:胖蔡说技术 » Python中的进程与线程
分享到: 更多 (0)

请小编喝杯咖啡~

支付宝扫一扫打赏

微信扫一扫打赏