Python中的线程模块之Threading模块(第3节)


Threading模块

在Python3中为了实现多线程, 可以通过_threadthreading两个标准库实现。在前面的教程中我们已经简单介绍了_thread模块的使用方法,_thread模块提供了低级别的、原始的线程以及一个简单的锁,它相比于threading模块的功能还是比较有限的。threading模块不仅包含_thread模块中的所有方法,还可以灵活地创建多线程程序,并且可以在多线程之间进行同步和通信。

threading模块实现多线程,主要是通过threading.Thread类实现。直接使用threading.Thread类的效果和_thread模块中的start_new_thread()方法效果一样。例如:

动手练一练:

import time
import threading

def fun(n, s):
    print("第", n, "个线程开始执行时间:", time.ctime())
    time.sleep(s)
    print("第", n, "个线程结束执行时间:", time.ctime())

def main():
    # 创建第一个Thread对象,通过target关键字参数指定线程函数fun,并通过args关键字参数传入参数
    a = threading.Thread(target = fun, args = (1, 2))
    # 启动第一个线程
    a.start()

    # 创建二个Thread对象
    b = threading.Thread(target = fun, args = (2, 4))
    b.start()

    # 等待线程函数执行完毕
    a.join()
    b.join()

# 当前模块直接被执行
if __name__ == "__main__":
    main()

执行以上代码,输出结果为:

 1 个线程开始执行时间 Mon Apr 29 10:49:48 2024
 2 个线程开始执行时间 Mon Apr 29 10:49:48 2024
 1 个线程结束执行时间 Mon Apr 29 10:49:50 2024
 2 个线程结束执行时间 Mon Apr 29 10:49:52 2024

从上面例子中的运行结果可以看出,通过threading.Thread类实现的效果和_thread模块实现的效果一模一样。在使用threading.Thread类之前,首先需要创建Thread类的实例对象,通过Thread类构造方法的target关键字参数执行线程函数,通过args关键字参数指定传给线程函数的参数。然后调用threading.Thread对象的start()方法启动线程,最后我们调用threading.Thread对象的join()方法等待两个线程函数都执行完毕后再退出程序。使用threading.Thread对象可以自动地帮助我们管理线程锁(创建锁、分配锁、获得锁、释放锁、检查锁等步骤)。可以看出,threading.Thread对象启动线程要比_thread模块中的锁方便很多。

在Python中,threading模块实现多线程,还可以通过继承threading.Thread类的方式,派生一个子类来创建线程。这种方法只要重写父类threading.Thread中的run()方法,然后再调用start()方法就能启动线程,并运行run()方法中的代码。例如:

动手练一练:

import time
import threading

# 从threading.Thread类派生一个子类MyThread
class MyThread(threading.Thread):
    def __init__(self, number, sec):
        # 调用父类的构造函数
        super(MyThread,self).__init__()
        self.number = number
        self.sec = sec

    # 重写父类的run方法
    def run(self):
        print("第", self.number, "个线程开始执行时间:", time.ctime())
        time.sleep(self.sec)
        print("第", self.number, "个线程结束执行时间:", time.ctime())

def main():
    # 创建第一个线程
    thread1 = MyThread(1, 2)
    # 启动线程
    thread1.start()

    # 创建第二个线程
    thread2 = MyThread(2, 4)
    thread2.start()

    # 等待两个线程执行完毕
    thread1.join()
    thread2.join()

# 当前模块直接被执行
if __name__ == "__main__":
    main()

执行以上代码,输出结果为:

 1 个线程开始执行时间 Mon Apr 29 11:56:32 2024
 2 个线程开始执行时间 Mon Apr 29 11:56:32 2024
 1 个线程结束执行时间 Mon Apr 29 11:56:34 2024
 2 个线程结束执行时间 Mon Apr 29 11:56:36 2024

上面的例子中,通过threading.Thread类的派生类MyThread实现的效果和直接通过threading.Thread类实现的效果一模一样。派生类MyThread中重写了父类threading.Thread的run()方法。使用线程的时候,先创建一个子线程MyThread类的对象,然后对象调用start()方法启动线程,调用一些内部的启动方法后再调用我们重写的run()方法。