猪行天下之Python基础——9.2 Python多线程与多进程(中)(一)

简介: 内容简述:1、threading模块详解2、queue模块详解

1、threading模块详解


Python提供的与线程操作相关的模块,网上有很多资料还是用的thread模块,在3.x版本中已经使用threading来替代thread,如果你在python 2.x版本想使用threading的话,可以使用dummy_threading模块


① threading模块提供的可直接调用的函数


  • active_count():获取当前活跃(alive)线程的个数。


  • current_thread():获取当前的线程对象。


  • get_ident():返回当前线程的索引,一个非零的整数(3.3新增)。


  • enumerate():获取当前所有活跃线程的列表。


  • main_thread():返回主线程对象(3.4新增)。


  • settrace(func):设置一个回调函数,在run()执行之前被调用。


  • setprofile(func):设置一个回调函数,在run()执行完毕之后调用。


  • stack_size():返回创建新线程时使用的线程堆栈大小。


  • threading.TIMEOUT_MAX:堵塞线程时间最大值,超过这个值会栈溢出。


② 线程局部变量(Thread-Local Data)


问题引入:


在一个进程内所有的线程共享进程的全局变量,线程间共享数据很方便但是每个线程都可以随意修改全局变量,可能会引起线程安全问题。


解决方法:


对于这种线程私有数据,最简单的方法就是对变量加锁或使用局部变量,只有线程自身可以访问,其他线程无法访问。除此之外还可以使用threading模块为我们提供的ThreadLocal变量,它本身是一个全局变量,但是线程们却可以使用它来保存私有数据。


用法简介:


定义一个全局变量:data = thread.local(),然后就可以往里面存数据啦,比如data.num = xxx,但是有一点要注意:如果data里没有设置对应的属性,直接取会报AttributeError异常,使用时可以捕获这个异常或先调用hasattr(对象,属性)判断对象中是否有该属性!使用代码示例如下:


import threading
import random
data = threading.local()
def show(d):
    try:
        num = d.num
    except AttributeError:
        print("线程 %s 还未设置该属性!" % threading.current_thread().getName())
    else:
        print("线程 %s 中该属性的值为 = %s" % (threading.current_thread().getName(), num))
def thread_call(d):
    show(d)
    d.num = random.randint(1, 100)
    show(d)
if __name__ == '__main__':
    show(data)
    data.num = 666
    show(data)
    for i in range(2):
        t = threading.Thread(target=thread_call, args=(data,), name="Thread " + str(i))
        t.start()


运行结果如下


线程 MainThread 还未设置该属性!
线程 MainThread 中该属性的值为 = 666
线程 Thread 0 还未设置该属性!
线程 Thread 0 中该属性的值为 = 80
线程 Thread 1 还未设置该属性!
线程 Thread 1 中该属性的值为 = 17


不同线程访问这个ThreadLocal变量,返回的都是不一样的值,原理:


threading.local()实例化一个全局对象,这个全局对象里有一个大字典,键值为两个弱引用对象{线程对象,字典对象},然后可以通过current_thread()获得当前的线程对象,然后根据这个对象可以拿到对应的字典对象,然后进行参数的读或者写。


③ 线程对象(threading.Thread)


创建新线程的两种方式:


  • 1.直接创建threading.Thread对象并把调用对象作为参数传入


  • 2.继承threading.Thread类重写run()方法


使用代码示例(验证单线程快还是多线程快):


import threading
import time
def catch_fish():
    pass
def one_thread():
    start_time = time.time()
    for i in range(1, 1001):
        catch_fish()
    end_time = time.time()
    print("单线程测试 耗时 === %s" % str(end_time - start_time))
def muti_thread():
    start_time = time.time()
    for i in range(1, 1001):
        threading.Thread(target=catch_fish()).start()
    end_time = time.time()
    print("多线程测试 耗时 === %s" % str(end_time - start_time))
if __name__ == '__main__':
    # 单线程
    threading.Thread(one_thread()).start()
    # 多线程
    muti_thread()


运行结果如下:


单线程测试 耗时 === 0.00011301040649414062
多线程测试 耗时 === 0.07665514945983887


从输出结果可以看到,多线程反而比单线程要慢,原因是前面介绍过的Python中的全局解释器锁(GIL), 使得任何时候仅有一个线程在执行。


Thread类构造函数


def __init__(self, group=None, target=None, name=None,
                 args=(), kwargs=None, *, daemon=None):


构造函数参数依次是:


  • group:线程组


  • target:要执行的函数


  • name:线程名字


  • args/kwargs:要传入的函数的参数


  • daemon:是否为守护线程


相关属性与函数:


  • start():启动线程,只能调用一次


  • run():线程执行的操作,可继承Thread重写,参数可从args和kwargs获取;


  • join([timeout]):堵塞调用线程,直到被调用线程运行结束或超时;如果
    没设置超时时间会一直堵塞到被调用线程结束。


  • name/getName():获得线程名;


  • setName():设置线程名;


  • ident:线程是已经启动,未启动会返回一个非零整数;


  • is_alive():判断是否在运行,启动后,终止前;


  • daemon/isDaemon():线程是否为守护线程;


  • setDaemon():设置线程为守护线程;


④ Lock(指令锁)与RLock(可重入锁)


在概念那里就讲了,多个进程并发的访问临界资源可能会引起线程同步安全问题,写个简单的例子,然后再引入同步锁。代码示例如下:


import threading
file_name = "test.txt"
# 定义一个写入文件的方法
def write_to_file(msg):
    try:
        with open(file_name, "a+", encoding="utf-8") as f:
            f.write(msg + "\n")
    except OSError as reason:
        print(str(reason))
class MyThread(threading.Thread):
    def __init__(self, msg):
        super().__init__()
        self.msg = msg
    def run(self):
        write_to_file(self.name + "~" + self.msg)
if __name__ == '__main__':
    for i in range(1, 21):
        t = MyThread(str(i)).start()


运行结果如下


# test.txt文件内容
Thread-1~1
Thread-5~5
Thread-3~3
Thread-2~2
Thread-4~4
Thread-6~6
Thread-7~7
Thread-8~8
Thread-10~10
Thread-9~9
Thread-11~11
Thread-13~13
Thread-12~12
Thread-14~14
Thread-15~15
Thread-16~16
Thread-17~17
Thread-19~19
Thread-20~20
Thread-18~18

发现结果并没有按照我们预想的1-20那样顺序打印,而是乱的,threading模块中提供了两个类来确保多线程共享资源的访问:「Lock」 和 「RLock」。


相关文章
|
4月前
|
数据采集 存储 JSON
Python爬取知乎评论:多线程与异步爬虫的性能优化
Python爬取知乎评论:多线程与异步爬虫的性能优化
|
4月前
|
人工智能 安全 调度
Python并发编程之线程同步详解
并发编程在Python中至关重要,线程同步确保多线程程序正确运行。本文详解线程同步机制,包括互斥锁、信号量、事件、条件变量和队列,探讨全局解释器锁(GIL)的影响及解决线程同步问题的最佳实践,如避免全局变量、使用线程安全数据结构、精细化锁的使用等。通过示例代码帮助开发者理解并提升多线程程序的性能与可靠性。
184 0
|
1月前
|
Java 测试技术 API
【JUC】(1)带你重新认识进程与线程!!让你深层次了解线程运行的睡眠与打断!!
JUC是什么?你可以说它就是研究Java方面的并发过程。本篇是JUC专栏的第一章!带你了解并行与并发、线程与程序、线程的启动与休眠、打断和等待!全是干货!快快快!
417 2
|
1月前
|
JSON 网络协议 安全
【Java】(10)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
143 1
|
1月前
|
JSON 网络协议 安全
【Java基础】(1)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
160 1
|
1月前
|
Java 调度 数据库
Python threading模块:多线程编程的实战指南
本文深入讲解Python多线程编程,涵盖threading模块的核心用法:线程创建、生命周期、同步机制(锁、信号量、条件变量)、线程通信(队列)、守护线程与线程池应用。结合实战案例,如多线程下载器,帮助开发者提升程序并发性能,适用于I/O密集型任务处理。
226 0
|
3月前
|
数据采集 消息中间件 并行计算
Python多线程与多进程性能对比:从原理到实战的深度解析
在Python编程中,多线程与多进程是提升并发性能的关键手段。本文通过实验数据、代码示例和通俗比喻,深入解析两者在不同任务类型下的性能表现,帮助开发者科学选择并发策略,优化程序效率。
259 1
|
4月前
|
监控 编译器 Python
如何利用Python杀进程并保持驻留后台检测
本教程介绍如何使用Python编写进程监控与杀进程脚本,结合psutil库实现后台驻留、定时检测并强制终止指定进程。内容涵盖基础杀进程、多进程处理、自动退出机制、管理员权限启动及图形界面设计,并提供将脚本打包为exe的方法,适用于需持续清理顽固进程的场景。
|
4月前
|
数据采集 监控 调度
干货分享“用 多线程 爬取数据”:单线程 + 协程的效率反超 3 倍,这才是 Python 异步的正确打开方式
在 Python 爬虫中,多线程因 GIL 和切换开销效率低下,而协程通过用户态调度实现高并发,大幅提升爬取效率。本文详解协程原理、实战对比多线程性能,并提供最佳实践,助你掌握异步爬虫核心技术。
|
5月前
|
JSON 算法 Java
打造终端里的下载利器:Python实现可恢复式多线程下载器
在数字时代,大文件下载已成为日常需求。本文教你用Python打造专业级下载器,支持断点续传、多线程加速、速度限制等功能,显著提升终端下载体验。内容涵盖智能续传、多线程分块下载、限速控制及Rich库构建现代终端界面,助你从零构建高效下载工具。
369 1

推荐镜像

更多
下一篇
oss云网关配置