百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

Python并发编程(3)——Python多线程详解介绍

bigegpt 2024-12-19 11:31 5 浏览

左手编程,右手年华。大家好,我是一点,关注我,带你走入编程的世界。
公众号:一点sir,关注领取python编程资料

Python 的多线程入门是非常简单的,直接导入threading模块就可以开始多线程之旅了。模块 threading 是标准库中的一个强大工具,它提供了丰富的接口来支持多线程编程。

Python多线程示例

以下是一个简单的多线程示例,我们就针对这个示例来讲解多线程的相关知识,这是一个非常简单的多线程例子,但是却包含了 Python 多线程的骨架,一些复杂的多线程代码都是从这些骨架衍生出去的。

import threading

def thread_task(num):
    for i in range(num):
        print(f"print thread: {i}")

def main():
    num = 10
    t = threading.Thread(target=thread_task, args=(num,))
    t.start()

    for i in range(0, -10, -1):
        print(f"print main: {i}")
    
    t.join()

    print("All threads have finished.")

if __name__ == "__main__":
    main()

运行该程序,会有下面输出:

print thread: 0
print thread: 1
print thread: 2print main: 0
print thread: 3

print thread: 4print main: -1
print thread: 5

print thread: 6print main: -2
print main: -3
print main: -4

print thread: 7
print thread: 8
print main: -5
print main: -6
print thread: 9
print main: -7
print main: -8
print main: -9

All threads have finished.

在上面示例中,我们定义了一个函数 thread_task,它接受一个参数 num 并在线程中打印消息。在 main 函数中我们启动所有线程,并使用 join() 方法等待它们完成,整体来讲这个代码非常的简单。

如果你多运行几次,会发现每次输出都是不一样的,不一样,比较正常,因为多线程会交替执行,只是打印有点奇怪,print thread: 2打印之后没有换行直接打印后面一条,具体原因后面会说,只要知道,这是一个 Python 多线程的代码就可以了。

创建线程

利用 Python 创建多线程是非常简单的,直接使用 threading.Thread 类创建线程对象,并将目标函数作为 target 参数传递。

thread = threading.Thread(target=thread_function, args=(i,))

这里的 thread_function 是做为函数参数传递进去的,至于什么时候执行这个函数,就是 Thread 内部的事情,对外表现就是调用 start 的时候执行。args=(i,) 是传递的参数,这里是个元组的形式进行传递的。

当然,我们也可以用参数 kwargs={key : value} 进行字典类型的参数传递,怎么方便怎么用吧。

thread = threading.Thread(target=thread_function, kwargs={"name" : "alittlesir"})

启动线程

创建线程之后,就可以通过调用线程对象的 start() 方法来启动线程。

thread.start()

这个方法没有太多可以介绍的,只不过如果大家有查看 threading 源码的话,可以看到有个 run 的方法,看字面意思好像也是启动线程用的,实际上你调用 start 方法的时候,这个方法也会被调用,它在新线程中被调用,用于执行线程的目标函数。当你创建一个 Thread 对象时,你可以通过 target 参数指定一个函数,这个函数将在新线程中执行。这个目标函数实际上会在 Thread 对象的 run() 方法中被调用。

也就是说你可以重写这个方法,多用于继承 threading.Thread,重写一个自己的线程类,实现一个自己的 run 方法,类似以下这样:

import threading
import time

class MyThread(threading.Thread):
    def __init__(self, name):
        super().__init__()
        self.name = name

    def run(self):
        print(f"Starting {self.name}")
        for i in range(5):  
            time.sleep(1)
            print(f"Thread {self.name} - {i}")
        print(f"Finished {self.name}")

def main():
    t1 = MyThread(name="Thread-1")
    t2 = MyThread(name="Thread-2")

    t1.start()  # 这将调用 t1.run()
    t2.start()  # 这将调用 t2.run()

    t1.join()
    t2.join()

if __name__ == "__main__":
    main()

在这个示例中,我们定义了一个 MyThread 类,它继承自 threading.Thread。我们重写了 run() 方法,以自定义线程的行为。然后,我们创建了两个 MyThread 实例并启动它们。每个线程都会执行其 run() 方法中的代码。

之所以在 Thread 类的 run 方法的存在是为了提供一种机制,使得用户可以通过继承 Thread 类并重写 run 方法来自定义线程的行为,可以理解为是一种扩展性的设计,平时可能不需要关系这个方法。

  • **start()**:用于启动线程,它会导致线程开始执行 run() 方法。
  • **run()**:是一个可以被重写的方法,用于定义线程的行为。通常不需要直接调用,除非你在调试或测试时需要手动触发线程的行为。

等待线程结束

调用线程对象的 join() 方法来等待线程结束,这确保了主程序在子线程完成之前不会退出。当一个线程对象调用 join() 方法时,主线程(通常是启动线程的那个线程)将会被阻塞,直到调用 join() 方法的线程完成其执行。

thread.join()

函数参数:

  • timeout:可选参数,指定等待线程完成的最大时间(以秒为单位)。如果指定了 timeoutjoin() 方法将在超时后返回,即使线程尚未完成。

所以可以知道,join方法的作用:

  1. 等待线程完成join() 方法确保主线程在当前线程完成之前不会继续执行。这在确保线程执行顺序或等待后台线程完成清理工作时非常有用。
  2. 防止主线程过早退出:在多线程程序中,如果主线程(程序运行的主要线程)先于子线程完成执行,程序可能会在子线程完成之前退出。使用 join() 方法可以确保主线程(或其他特定线程)等待所有子线程完成后再退出。

既然 join 方法会阻塞主线程,那么没有join方法,而主线程比子线程先跑完,这时候子线程会怎么样嗯?直接退出还是继续跑完呢?

答案是会继续跑完!

在 Python 中,当主线程执行完毕时,它会等待所有通过 threading 模块启动的子线程完成,然后才会退出程序。这是因为 threading 模块会确保所有由它管理的线程都完成,即使没有显式调用 join() 方法。

这是一个例子,展示了即使没有使用 join() 方法,子线程仍然会执行完毕:

import threading
import time

def thread_function():
    print("Thread starting")
    time.sleep(2)
    print("Thread finishing")

def main():
    print("Main starting")
    thread = threading.Thread(target=thread_function)
    thread.start()
    # 注意这里没有调用 thread.join()
    print("Main finishing")

if __name__ == "__main__":
    main()

既然没有join方法,子线程也会继续跑,那为啥还需要这个方法呢?

技术是为人服务的,所以程序的控制权是在人这边,一定要按照人预期的方式去运行,我们的预期是子线程跑完在跑主线程,就这么简单。

说一千道一万,即使没有显式调用 join() 方法,Python 的 threading 模块也会确保所有子线程在程序退出前完成执行。但是,为了代码的清晰性和可靠性,显式使用 join() 方法来等待特定线程完成仍然是一个好习惯。

奇怪的打印

现在回到开头的那个示例,我们留下了一个疑问,每次运行的时候,输出结果有点奇怪,print thread: 2打印之后按理说应该是换行,然后再输出print main: 0,可是这里没有换行,造成了非常奇怪的一个输出,这里就涉及了线程同步的相关问题了。

相关推荐

了解Linux目录,那你就了解了一半的Linux系统

大到公司或者社群再小到个人要利用Linux来开发产品的人实在是多如牛毛,每个人都用自己的标准来配置文件或者设置目录,那么未来的Linux则就是一团乱麻,也对管理造成许多麻烦。后来,就有所谓的FHS(F...

Linux命令,这些操作要注意!(linux命令?)

刚玩Linux的人总觉得自己在演黑客电影,直到手滑输错命令把公司服务器删库,这才发现命令行根本不是随便乱用的,而是“生死簿”。今天直接上干货,告诉你哪些命令用好了封神!喜欢的一键三连,谢谢观众老爷!!...

Linux 命令速查手册:这 30 个高频指令,拯救 90% 的运维小白!

在Linux系统的世界里,命令行是强大的武器。对于运维小白而言,掌握一些高频使用的Linux命令,能极大提升工作效率,轻松应对各种系统管理任务。今天,就为大家奉上精心整理的30个Linu...

linux必学的60个命令(linux必学的20个命令)

以下是Linux必学的20个基础命令:1.cd:切换目录2.ls:列出文件和目录3.mkdir:创建目录4.rm:删除文件或目录5.cp:复制文件或目录6.mv:移动/重命名文件或目录7....

提高工作效率的--Linux常用命令,能够决解95%以上的问题

点击上方关注,第一时间接受干货转发,点赞,收藏,不如一次关注评论区第一条注意查看回复:Linux命令获取linux常用命令大全pdf+Linux命令行大全pdf为什么要学习Linux命令?1、因为Li...

15 个实用 Linux 命令(linux命令用法及举例)

Linux命令行是系统管理员、开发者和技术爱好者的强大工具。掌握实用命令不仅能提高效率,还能解锁Linux系统的无限潜力,本文将深入介绍15个实用Linux命令。ls-列出目录内容l...

Linux 常用命令集合(linux常用命令全集)

系统信息arch显示机器的处理器架构(1)uname-m显示机器的处理器架构(2)uname-r显示正在使用的内核版本dmidecode-q显示硬件系统部件-(SMBIOS/DM...

Linux的常用命令就是记不住,怎么办?

1.帮助命令1.1help命令#语法格式:命令--help#作用:查看某个命令的帮助信息#示例:#ls--help查看ls命令的帮助信息#netst...

Linux常用文件操作命令(linux常用文件操作命令有哪些)

ls命令在Linux维护工作中,经常使用ls这个命令,这是最基本的命令,来写几条常用的ls命令。先来查看一下使用的ls版本#ls--versionls(GNUcoreutils)8.4...

Linux 常用命令(linux常用命令)

日志排查类操作命令查看日志cat/var/log/messages、tail-fxxx.log搜索关键词grep"error"xxx.log多条件过滤`grep-E&#...

简单粗暴收藏版:Linux常用命令大汇总

号主:老杨丨11年资深网络工程师,更多网工提升干货,请关注公众号:网络工程师俱乐部下午好,我的网工朋友在Linux系统中,命令行界面(CLI)是管理员和开发人员最常用的工具之一。通过命令行,用户可...

「Linux」linux常用基本命令(linux常用基本命令和用法)

Linux中许多常用命令是必须掌握的,这里将我学linux入门时学的一些常用的基本命令分享给大家一下,希望可以帮助你们。总结送免费学习资料(包含视频、技术学习路线图谱、文档等)1、显示日期的指令:d...

Linux的常用命令就是记不住,怎么办?于是推出了这套教程

1.帮助命令1.1help命令#语法格式:命令--help#作用:查看某个命令的帮助信息#示例:#ls--help查看ls命令的帮助信息#netst...

Linux的30个常用命令汇总,运维大神必掌握技能!

以下是Linux系统中最常用的30个命令,精简版覆盖日常操作核心需求,适合快速掌握:一、文件/目录操作1.`ls`-列出目录内容`ls-l`(详细信息)|`ls-a`(显示隐藏文件)...

Linux/Unix 系统中非常常用的命令

Linux/Unix系统中非常常用的命令,它们是进行文件操作、文本处理、权限管理等任务的基础。下面是对这些命令的简要说明:**文件操作类:*****`ls`(list):**列出目录内容,显...