Python学习笔记#7:多线程 Threading

本文介绍了Python中的Threading模块,包括如何创建线程、守护线程与非守护线程的区别以及线程阻塞join()的使用。通过实例展示了线程的运行逻辑,强调了守护线程在程序退出时会被强制关闭,而非守护线程会等待其结束才退出。还讨论了join()的timeout参数对线程执行的影响。
摘要由CSDN通过智能技术生成

Python内置库:threading

Python的线程操作在旧版本中使用的是thread模块,在Python27和Python3中引入了threading模块,同时thread模块在Python3中改名为_thread模块,threading模块相较于thread模块,对于线程的操作更加的丰富,而且threading模块本身也是相当于对thread模块的进一步封装而成,thread模块有的功能threading模块也都有,所以涉及到对线程的操作,推荐使用threading模块。

threading模块中包含了关于线程操作的丰富功能,包括:常用线程函数,线程对象,锁对象,递归锁对象,事件对象,条件变量对象,信号量对象,定时器对象,栅栏对象。

注:本文使用的Python版本是Python3.6

多线程

1. 创建线程

创建线程,target可以将回调函数作为参数导入线程,如果回调函数带参数,则使用args

import time
import threading


def test_thread(para="", sleep=0):
    """线程运行函数"""
    print("{} 开始睡眠 {} 秒".format(threading.current_thread().getName(), sleep))
    time.sleep(sleep)
    print("{} 睡眠 {} 秒结束,打印结果:{}".format(threading.current_thread().getName(), sleep, para))


def main():
    # 创建线程
    Thread_1 = threading.Thread(target=test_thread, args=("Thread-1", 5))
    Thread_2 = threading.Thread(target=test_thread, args=("Thread-2", 3))
    Thread_3 = threading.Thread(target=test_thread, args=("Thread-3", 1))
    
    # 启动线程
    Thread_1.start()
    Thread_2.start()
    Thread_3.start()

if __name__ == '__main__':
    main()

结果:线程1、2、3同时开始、分别睡眠5、3、1秒,所以3线程最先结束,1线程最后结束。

Thread-1 开始睡眠 5 秒
Thread-2 开始睡眠 3 秒
Thread-3 开始睡眠 1 秒
Thread-3 睡眠 1 秒结束,打印结果:Thread-3
Thread-2 睡眠 3 秒结束,打印结果:Thread-2
Thread-1 睡眠 5 秒结束,打印结果:Thread-1

2. 守护线程与非守护线程

  • 守护线程:
    只有所有守护线程都结束,整个Python程序才会退出,但并不是说Python程序会等待守护线程运行完毕,相反,当程序退出时,如果还有守护线程在运行,程序会去强制终结所有守护线程,当守所有护线程都终结后,程序才会真正退出。可以通过修改daemon属性或者初始化线程时指定daemon参数来指定某个线程为守护线程。

  • 非守护线程:
    一般创建的线程默认就是非守护线程,包括主线程也是,即在Python程序退出时,如果还有非守护线程在运行,程序会等待直到所有非守护线程都结束后才会退出。

注:守护线程会在程序关闭时突然关闭(如果守护线程在程序关闭时还在运行),它们占用的资源可能没有被正确释放,比如正在修改文档内容等,需要谨慎使用。

import time
import threading

def test_thread(para="", sleep=0):
    """线程运行函数"""
    print("{} 开始睡眠 {} 秒".format(threading.current_thread().getName(), sleep))
    time.sleep(sleep)
    print("{} 睡眠 {} 秒结束,打印结果:{}".format(threading.current_thread().getName(), sleep, para))

def main():
    # 创建线程
    Thread_1 = threading.Thread(target=test_thread, args=("Thread-1", 5))
    Thread_2 = threading.Thread(target=test_thread, args=("Thread-2", 3))
    Thread_3 = threading.Thread(target=test_thread, args=("Thread-3", 1))

    Thread_1.setDaemon(True)
    Thread_2.setDaemon(True)
    Thread_3.setDaemon(True)

    # 启动线程
    Thread_1.start()
    Thread_2.start()
    Thread_3.start()

if __name__ == '__main__':
    main()

结果:1、2、3线程都是守护线程,但是主线程不是守护线程,主线程启动1、2、3线程以后就结束了,由于1、2、3都是守护线程,所以也被强制性关掉。

Thread-1 开始睡眠 5 秒
Thread-2 开始睡眠 3 秒
Thread-3 开始睡眠 1

3. 守护线程与非守护线程的区别

import time
import threading

def test_thread(para="", sleep=0):
    """线程运行函数"""
    print("{} 开始睡眠 {} 秒".format(threading.current_thread().getName(), sleep))
    time.sleep(sleep)
    print("{} 睡眠 {} 秒结束,打印结果:{}".format(threading.current_thread().getName(), sleep, para))

def main():
    # 创建线程
    Thread_1 = threading.Thread(target=test_thread, args=("Thread-1", 5))
    Thread_2 = threading.Thread(target=test_thread, args=("Thread-2", 3))
    Thread_3 = threading.Thread(target=test_thread, args=("Thread-3", 1))

    Thread_1.setDaemon(True)
    # Thread_2.setDaemon(True)
    # Thread_3.setDaemon(True)
    
    # 启动线程
    Thread_1.start()
    Thread_2.start()
    Thread_3.start()

if __name__ == '__main__':
    main()

结果:主线程启动1、2、3以后就结束,其中1为守护线程,所以被强制结束,但是2、3是非守护线程,所以2、3正常运行。

Thread-1 开始睡眠 5 秒
Thread-2 开始睡眠 3 秒
Thread-3 开始睡眠 1 秒
Thread-3 睡眠 1 秒结束,打印结果:Thread-3
Thread-2 睡眠 3 秒结束,打印结果:Thread-2

4. 线程阻塞 join()

线程阻塞就是让一个线程优先运行,直到它结束以后,才运行后面的程序,如上面的列子,正常情况下线程1是不会运行的,但如果我们加入阻塞,程序就会优先等线程1结束,再运行后面的语句。

import time
import threading

def test_thread(para="", sleep=0):
    """线程运行函数"""
    print("{} 开始睡眠 {} 秒".format(threading.current_thread().getName(), sleep))
    time.sleep(sleep)
    print("{} 睡眠 {} 秒结束,打印结果:{}".format(threading.current_thread().getName(), sleep, para))

def main():
    # 创建线程
    Thread_1 = threading.Thread(target=test_thread, args=("Thread-1", 5))
    Thread_2 = threading.Thread(target=test_thread, args=("Thread-2", 3))
    Thread_3 = threading.Thread(target=test_thread, args=("Thread-3", 1))

    Thread_1.setDaemon(True)
    # Thread_2.setDaemon(True)
    # Thread_3.setDaemon(True)

    # 启动线程
    Thread_1.start()
    # 线程1阻塞
    Thread_1.join()
    Thread_2.start()
    Thread_3.start()

if __name__ == '__main__':
    main()

结果:线程1阻塞以后,主线程相当于中断,直到线程1结束以后,再往后运行。

Thread-1 开始睡眠 5 秒
Thread-1 睡眠 5 秒结束,打印结果:Thread-1
Thread-2 开始睡眠 3 秒
Thread-3 开始睡眠 1 秒
Thread-3 睡眠 1 秒结束,打印结果:Thread-3
Thread-2 睡眠 3 秒结束,打印结果:Thread-2

timeout

timeout 表示阻塞的最大时间,我们可以简单计算一下,线程1、2、3分辨需要5、3、1秒,默认情况下(没有timeout)系统会阻塞线程1直到它结束位置,但是如果我们设置一个最大时间,假设

timeout=1

    # 启动线程
    Thread_1.start()
    # 线程1阻塞
    Thread_1.join(timeout=1)
    Thread_2.start()
    Thread_3.start()

0秒:开始线程1,睡眠5秒,阻塞1秒
1秒:主线程开始线程2,睡眠3秒,开始线程3,睡眠1秒
2秒:线程3结束
4秒:线程2结束,主线程结束,线程1强制关闭

所以实际结果:

Thread-1 开始睡眠 5 秒
Thread-2 开始睡眠 3 秒
Thread-3 开始睡眠 1 秒
Thread-3 睡眠 1 秒结束,打印结果:Thread-3
Thread-2 睡眠 3 秒结束,打印结果:Thread-2

timeout=2

    # 启动线程
    Thread_1.start()
    # 线程1阻塞
    Thread_1.join(timeout=2)
    Thread_2.start()
    Thread_3.start()

0秒:开始线程1,睡眠5秒,阻塞2秒
2秒:主线程开始线程2,睡眠3秒,开始线程3,睡眠1秒
3秒:线程3结束
5秒:线程2结束,线程1结束,全部线程结束

实际结果:

Thread-1 开始睡眠 5 秒
Thread-2 开始睡眠 3 秒
Thread-3 开始睡眠 1 秒
Thread-3 睡眠 1 秒结束,打印结果:Thread-3
Thread-2 睡眠 3 秒结束,打印结果:Thread-2
Thread-1 睡眠 5 秒结束,打印结果:Thread-1
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值