# Python学习笔记 Day13

进程和线程

Python学习笔记 Day13

进程和线程

概念

Python 中的多进程

Pythonos模块提供了fork()函数。由于Windows系统没有fork()调用,因此要实现跨平台的多进程编程,可以使用multiprocessing模块的Process类来创建子进程,而且该模块还提供了更高级的封装,例如批量启动进程的进程池(Pool)、用于进程间通信的队列(Queue)和管道(Pipe)等。

在程序中,用到了 os 模块的 getpid 函数用来查号看进程号。

在教程中,多进程通过 multiprocessing 模块的 Process 类来实现。

Python
 p1 = Process(target=download_task, args=('Python从入门到住院.pdf', ))
 p1.start()
 t1.join()
 # target 表示需要执行的函数, args 表示需要传递给这个函数的参数, start 方法启动进程, join 方法表示等待进程执行结束。 
 

Python 中的多线程

代码中使用的方法和多进程并没有太大区别。所以这里主要说了一下创建自定义线程的类。我们可以直接使用 threading 模块的Thread类来创建线程,但是我们之前讲过一个非常重要的概念叫“继承”,我们可以从已有的类创建新类,因此也可以通过继承Thread类的方式来创建自定义的线程类,然后再创建线程对象并启动线程。代码如下所示。

Python
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()

所谓“临界资源”,就是被多个线程竞争使用的资源,所以对齐的访问需要加上保护。

Python
 def deposit(self, money):
        # 先获取锁才能执行后续的代码
        self._lock.acquire()
        try:
            new_balance = self._balance + money
            sleep(0.01)
            self._balance = new_balance
        finally:
            # 在finally中执行释放锁的操作保证正常异常锁都能释放
            self._lock.release()

    @property
    def balance(self):
        return self._balance

众所周知, python 并不是一个非常高效的语言,他的多线程并不能发挥CPU的多核特性。因为Python的解释器有一个“全局解释器锁”(GIL)的东西,任何线程执行前必须先获得GIL锁,然后每执行100条字节码,解释器就自动释放GIL锁,让别的线程有机会执行。

多进程和多线程的选择

任务分为计算密集型和I/O密集型。计算密集型任务的特点是要进行大量的计算,消耗CPU资源,比如对视频进行编码解码或者格式转换等等,这种任务全靠CPU的运算能力,虽然也可以用多任务完成,但是任务越多,花在任务切换的时间就越多,CPU执行任务的效率就越低。计算密集型任务由于主要消耗CPU资源,这类任务用Python这样的脚本语言去执行效率通常很低,最能胜任这类任务的是C语言,我们之前提到了Python中有嵌入C/C++代码的机制。

除了计算密集型任务,其他的涉及到网络、存储介质I/O的任务都可以视为I/O密集型任务,这类任务的特点是CPU消耗很少,任务的大部分时间都在等待I/O操作完成(因为I/O的速度远远低于CPU和内存的速度)。对于I/O密集型任务,如果启动多任务,就可以减少I/O等待时间从而让CPU高效率的运转。有一大类的任务都属于I/O密集型任务,这其中包括了我们很快会涉及到的网络应用和Web应用。

实战中遇到的问题

喜闻乐见出Bug了。

ModuleNotFoundError: No module named 'tkinter'

之前遇到这种情况的时候,一般就是 pip install xxx搞定,但是这次显然不行。最近风头比较紧,所以只能面向Bing的编程了,所幸搜到一篇 csdn 里的文章:Python下"No module named _tkinter"问题解决过程总结

什么是tkinter, tcl ,tk

tkinter其实是Python调用tcl程序的标准Python程序,可以通过这个interface调用tcl的程序,因为在大多数的unix系统中都内置了很多的tcl程序和命令。

Tcl 是“工具控制语言(Tool Command Language)”的缩写,其面向对象为otcl语言。Tk 是 Tcl“图形工具箱”的扩展,它提供各种标准的 GUI 接口项,以利于迅速进行高级应用程序开发。

Cmd
python -m tkinter
# 调用 tkinter 的 modele

解决问题的方法

有人提示说 python-tk/python3-tk 的类库需要在操作系统层面进行安装,这个时候我突然希望自己是 ubuntu 的系统了。不过windows的解决方案很简单,直接打开 Python 的安装文件 Modify 一下就好了。

QQ截图20191004220748.jpg

最后编辑于
文章链接: http://pheustal.com/2019/10-03/pythonday13
本作品采用CC-BY-SA许可。