【Python】多线程的实现及其存在的问题

1,940 阅读2分钟

「这是我参与2022首次更文挑战的第6天,活动详情查看:2022首次更文挑战」。

Python 多线程

python主要是通过thread和threading这两个模块来实现多线程支持。python的thread模块是比较底层的模块,python的threading模块是对thread做了一些封装,可以更加方便的被使用。

创建新的线程有两种方法:

  1. 通过 threading.Thread() 传递给 Thread 对象一个可执行方法(或对象)
  2. 继承threading.Thread 定义子类并重写 run() 方法

threading.Thread()

主要参数:

  • target :Callable,可执行方法
  • name :str,线程名
  • args :Iterable[Any],可执行方法的参数列表
  • daemon :bool,这个线程是否是守护线程

示例代码

import random
import threading
from time import sleep


def myPrint(odd: bool):
    """odd 为 True 输出奇数,否则输出偶数"""
    for i in range(10):
        if odd and i % 2 == 1:
            print(i)
        elif not odd and i % 2 == 0:
            print(i)
        sleep(random.random())

t1 = threading.Thread(target= myPrint, args=(True, ))
t2 = threading.Thread(target= myPrint, args=(False, ))
t1.start()
t2.start()

运行代码,奇数和偶数混合输出,而不是先输出全部奇数,再输出全部偶数。

继承threading.Thread

通过继承 threading.Thread 定义子类创建多线程,直接从 threading.Thread 继承,然后重写 init 方法和 run 方法。因为继承了 threading.Thread ,调用 start方法时,会自动运行其 run 方法中的代码。

示例代码:

class myCPrint(threading.Thread):
    def __init__(self , odd):
        super(myCPrint,self).__init__()
        self.odd = odd

    def run(self):
        for i in range(10):
            if self.odd and i % 2 == 1:
                print(i)
            elif not self.odd and i % 2 == 0:
                print(i)
            sleep(random.random())

c1 = myCPrint(True)
c2 = myCPrint(False)
c1.start()
c2.start()

运行代码,同样可以看到奇数和偶数混合输出,而不是先输出全部奇数,再输出全部偶数。

Python 多线程存在的问题

Python中的多线程是假的多线程

Python 代码的执行由 Python 虚拟机(解释器)来控制。Python在设计之初就考虑要在主循环中,同时只有一个线程在执行。对 Python 虚拟机的访问由全局解释器锁(GIL)来控制,正是这个锁保证同时只有一个线程在运行。在多线程环境中,Python虚拟机按照以下方式执行:

  1. 设置GIL
  2. 切换到一个线程并执行
  3. 线程执行完成或设置为睡眠状态(最多执行100条字节码)
  4. 解锁GIL
  5. 重复 1-4

不管有几个核,单位时间多个核只能跑一个线程,然后时间片轮转。

如果需要充分利用多核,可使用 multiprocessing 库,创建多进程。