threading库:Python线程的基础知识

简介: threading库:Python线程的基础知识

前言


前面的subprocess库主要讲解的是进程知识与进程间的交互。而进程有可以拥有多个线程,所以threading库提供了管理多个线程执行的API,允许程序在同一个进程空间并发地运行多个操作。


本篇,将详细的介绍Python线程库:threading。


Thread对象


要使用threading库,最简单的方式是使用Thread,它可以直接通过函数实例化一个Thread对象,并调用start让它工作。毕竟,我们用线程也是为了执行耗时任务,把任务封装到一个函数中,直接创建往往最简单。


示例如下:

import threading
def print1tonum(num):
    for i in range(10000):
        print(i)
t = threading.Thread(target=print1tonum,args=(10000,))
t.start()


运行之后,效果如下:


这里,我们创建了一个线程,并向它传递参数告诉它要完成什么工作。


区分线程


一般来说,我们创建线程是避免在主线程中处理耗时的任务,但是有时候,就算是基本的运算任务,因为其叠加起来非常的多,我们会考虑用多个线程进行处理。示例如下:

import threading
import time
def printThreadName1():
    print(threading.current_thread().getName() + " start")
    time.sleep(0.2)
    print(threading.current_thread().getName() + ' end')
def printThreadName2():
    print(threading.current_thread().getName() + " start")
    time.sleep(0.2)
    print(threading.current_thread().getName() + ' end')
def printThreadName3():
    print(threading.current_thread().getName() + " start")
    time.sleep(0.2)
    print(threading.current_thread().getName() + ' end')
t1 = threading.Thread(name='t1', target=printThreadName1)
t2 = threading.Thread(name='t2', target=printThreadName2)
t3 = threading.Thread(name='t3', target=printThreadName3)
t1.start()
t2.start()
t3.start()


运行之后,效果如下:


这里,我们会发现print打印非常的混乱,虽然3个线程都是一摸一样的,但结束的时候并不是按顺序结束的,因为它们是同时运行的。(这里需要注意,更多内容接着往下看)


守护线程


运行上面的代码我们会发现,主程序都是在等线程运行完成之后,才结束的。也就是说,创建线程的主程序,无法在线程结束前安全退出。那么,可不可保证运行线程时,主线程可以退出呢?


答案是可以的,这个时候我们需要用到守护线程,这个线程可以一直运行而不阻塞主程序的退出,比如在服务器监控的工具线程,对于这些服务,守护线程往往更有用。


要构造守护线程,需要将上面创建线程的方式增加一个参数daemon,它是一个布尔值,默认值为False,普通线程,改为True就是守护线程。

import threading
import time
def printThreadName1():
    print(threading.current_thread().getName() + " start")
    time.sleep(0.2)
    print(threading.current_thread().getName() + ' end')
def printThreadName2():
    print(threading.current_thread().getName() + " start")
    time.sleep(0.2)
    print(threading.current_thread().getName() + ' end')
def printThreadName3():
    print(threading.current_thread().getName() + " start")
    time.sleep(0.2)
    print(threading.current_thread().getName() + ' end')
t1 = threading.Thread(name='t1', target=printThreadName1, daemon=True)
t2 = threading.Thread(name='t2', target=printThreadName2)
t3 = threading.Thread(name='t3', target=printThreadName3)
t1.start()
t2.start()
t3.start()


运行之后,效果如下:

可以看到,博主这里将t1设置为守护线程,但是没有看到t1结束主程序就结束了。如果需要等待一个守护线程完成工作,可以增加join()函数。

t1 = threading.Thread(name='t1', target=printThreadName1, daemon=True)
t2 = threading.Thread(name='t2', target=printThreadName2, daemon=True)
t3 = threading.Thread(name='t3', target=printThreadName3, daemon=True)
t1.start()
t2.start()
t3.start()
t1.join()
t2.join()
t3.join()


输出结果就不展示了,与前文非守护的线程一样。


需要注意的是,join()函数会无限阻塞,直到线程任务结束。当然,如果想设置一个最大的等待时间,超过时间就不在等待也行。


示例如下:

t1 = threading.Thread(name='t1', target=printThreadName1, daemon=True)
t2 = threading.Thread(name='t2', target=printThreadName2, daemon=True)
t3 = threading.Thread(name='t3', target=printThreadName3, daemon=True)
t1.start()
t2.start()
t3.start()
t1.join(0.4)
print(t1.is_alive())
t2.join()
t3.join()


这里,设置了t1等待最长时间为0.4秒,读者可以将t1线程的运行时间增加到1秒,看看其效果,is_alive()的意义是等待了0.4秒后,t1是否还在运行,为True代表是的,如下所示:


需要注意的是join()可能会造成死锁。比如现在有3个线程t1,t2,t3,t1需要使用资源12,t2需要使用资源23,t3需要使用资源13,3个同时运行。开始时,t1在使用资源1等待资源2,t2在使用资源2等待资源3,t3在使用资源3等待资源1,它们互相等待对方释放资源,但都不释放,导致循环等待下去,形成死锁。(与主线程等待线程结束差不多的道理)


自定义线程


从上面的所有线程运行,想必读者肯定发现了一个问题,那就是threading.Thread无法提供返回值。而实际的多线程运行中,往往我们都是获取网络数据,然后再处理,所以必须获取其处理的结果。


这个时候,自定义线程就能实现该需求,示例如下:

import threading
import requests
class GetHTMLThread(threading.Thread):
    def __init__(self, args, kwargs):
        super(GetHTMLThread, self).__init__()
        self.args = args
        self.kwargs = kwargs
    def run(self):
        print(self.args)
        self.result = requests.get(url=self.kwargs['url'])
    def get_result(self):
        return self.result.text
t = GetHTMLThread(args=(1,), kwargs={'url': "https://www.baidu.com"})
t.start()
t.join()
print(t.get_result())


运行之后,效果如下:


定时器线程


在我们使用django搭建服务器时,往往有许多的延时触发任务。比如我们CSDN就是我们搭建的博客网站,那么我们定时更新博客就是一个延时任务,我昨天写完博客,希望明天下午18点发送,那么这个延时任务就是中间的时差。


那么,此时我们使用定时器线程往往效果更好。示例如下:

import threading
def getWeather():
    print("更新博客")
t1 = threading.Timer(0.3, getWeather)
t1.setName('t1')
t2 = threading.Timer(0.3, getWeather)
t2.setName('t2')
t1.start()
t2.start()
t2.cancel()


运行之后,效果如下:


这里会延迟0.3秒执行线程,而之所以t2线程没有执行,是因为我们调用了cancel()函数,取消了该线程的执行。


线程间传送信号


尽管使用多线程的目的是并发地运行单独的任务,但有时候也需要在多个线程间同步操作。而Python中,线程的通信方法是事件对象。


Event管理一个内部标志,调用者可以用set()和clear()方法控制这个标志。其他线程可以使用wait()暂停,直到这个标志被设置,可以有效的阻塞进程直到允许这些线程继续。

import threading
import time
def wait_for_event(e):
    print("wait_for_event")
    event_is_set = e.wait()
    print("wait_for_event", event_is_set)
def wait_for_event_timeout(e, t):
    while not e.is_set():
        print("wait_for_event_timeout")
        event_is_set = e.wait(t)
        print("wait_for_event_timeout", event_is_set)
        if event_is_set:
            print("运行任务")
        else:
            print('运行其他任务')
e = threading.Event()
t1 = threading.Thread(
    name='t1',
    target=wait_for_event,
    args=(e,)
)
t1.start()
t2 = threading.Thread(
    name='t2',
    target=wait_for_event_timeout,
    args=(e, 2)
)
t2.start()
time.sleep(5)
e.set()


运行之后,效果如下:


这个例子中,wait_for_event_timeout()函数将检查事件状态而不会无限阻塞。wait_for_event()在wait()调用的位置阻塞,事件状态改变之前它不会返回。


因为后面的线程知识还有很多,但这篇博文已经很长了,所以接下来的锁,释放锁等知识,将在下一个threading库章节讲解。

相关文章
|
2月前
|
存储 人工智能 测试技术
如何使用LangChain的Python库结合DeepSeek进行多轮次对话?
本文介绍如何使用LangChain结合DeepSeek实现多轮对话,测开人员可借此自动生成测试用例,提升自动化测试效率。
429 125
如何使用LangChain的Python库结合DeepSeek进行多轮次对话?
|
2月前
|
监控 数据可视化 数据挖掘
Python Rich库使用指南:打造更美观的命令行应用
Rich库是Python的终端美化利器,支持彩色文本、智能表格、动态进度条和语法高亮,大幅提升命令行应用的可视化效果与用户体验。
204 0
|
1月前
|
数据可视化 关系型数据库 MySQL
【可视化大屏】全流程讲解用python的pyecharts库实现拖拽可视化大屏的背后原理,简单粗暴!
本文详解基于Python的电影TOP250数据可视化大屏开发全流程,涵盖爬虫、数据存储、分析及可视化。使用requests+BeautifulSoup爬取数据,pandas存入MySQL,pyecharts实现柱状图、饼图、词云图、散点图等多种图表,并通过Page组件拖拽布局组合成大屏,支持多种主题切换,附完整源码与视频讲解。
212 4
【可视化大屏】全流程讲解用python的pyecharts库实现拖拽可视化大屏的背后原理,简单粗暴!
|
1月前
|
传感器 运维 前端开发
Python离群值检测实战:使用distfit库实现基于分布拟合的异常检测
本文解析异常(anomaly)与新颖性(novelty)检测的本质差异,结合distfit库演示基于概率密度拟合的单变量无监督异常检测方法,涵盖全局、上下文与集体离群值识别,助力构建高可解释性模型。
299 10
Python离群值检测实战:使用distfit库实现基于分布拟合的异常检测
|
1月前
|
Java 调度 数据库
Python threading模块:多线程编程的实战指南
本文深入讲解Python多线程编程,涵盖threading模块的核心用法:线程创建、生命周期、同步机制(锁、信号量、条件变量)、线程通信(队列)、守护线程与线程池应用。结合实战案例,如多线程下载器,帮助开发者提升程序并发性能,适用于I/O密集型任务处理。
227 0
|
3月前
|
运维 Linux 开发者
Linux系统中使用Python的ping3库进行网络连通性测试
以上步骤展示了如何利用 Python 的 `ping3` 库来检测网络连通性,并且提供了基本错误处理方法以确保程序能够优雅地处理各种意外情形。通过简洁明快、易读易懂、实操性强等特点使得该方法非常适合开发者或系统管理员快速集成至自动化工具链之内进行日常运维任务之需求满足。
231 18
|
3月前
|
数据采集 消息中间件 并行计算
Python多线程与多进程性能对比:从原理到实战的深度解析
在Python编程中,多线程与多进程是提升并发性能的关键手段。本文通过实验数据、代码示例和通俗比喻,深入解析两者在不同任务类型下的性能表现,帮助开发者科学选择并发策略,优化程序效率。
259 1
|
Python
pip批量安装Python库 requirement.txt 离线环境无互联网环境下pip安装Python库
pip批量安装Python库 requirement.txt 离线环境无互联网环境下pip安装Python库
809 3
|
开发工具 git Python
安装和使用`libnum`是一个用于数字理论函数的Python库
【6月更文挑战第19天】`libnum`是Python的数字理论函数库。安装可通过`git clone`,进入目录后运行`python setup.py install`,也可用`pip install libnum`。示例:使用`int_to_hex`将十进制数42转换为十六进制字符串'2a'。注意,信息可能已过时,应查最新文档以确保准确性。如遇问题,参考GitHub仓库或寻求社区帮助。
337 1
确保你已经安装了`python-barcode`库。如果没有,可以通过pip来安装:
确保你已经安装了`python-barcode`库。如果没有,可以通过pip来安装:

热门文章

最新文章

推荐镜像

更多