python创建线程和结束线程

👽发现宝藏

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。

python创建线程和结束线程

在 Python 中,线程是一种轻量级的执行单元,允许我们在程序中同时执行多个任务。线程的创建和结束是多线程编程中的核心概念之一。在本文中,我们将学习如何使用 Python 创建线程,并探讨如何优雅地结束线程。

创建线程

Python 中创建线程非常简单,可以使用 threading 模块来实现。下面是一个简单的例子:

import threading
import time

def print_numbers():
    for i in range(1, 6):
        print(i)
        time.sleep(1)

# 创建线程
thread = threading.Thread(target=print_numbers)

# 启动线程
thread.start()

# 主线程等待子线程执行完成
thread.join()

print("线程执行完成!")

在这个例子中,我们创建了一个名为 print_numbers 的函数,该函数用于打印 1 到 5 的数字。然后,我们使用 threading.Thread 类创建了一个新的线程,并指定了要执行的目标函数。最后,通过调用 start() 方法启动线程,通过 join() 方法等待线程执行完成。

结束线程

结束线程通常是为了让程序在不需要线程继续执行时能够正常退出,或者在特定条件下终止线程的执行。在 Python 中,线程是无法直接终止的,但是可以通过设置标志位或者发送信号的方式让线程自行退出。下面是一个简单的例子:

import threading
import time

# 全局标志位,控制线程执行
is_running = True

def count_numbers():
    i = 1
    while is_running:
        print(i)
        i += 1
        time.sleep(1)

# 创建线程
thread = threading.Thread(target=count_numbers)

# 启动线程
thread.start()

# 主线程等待一段时间后修改标志位,结束线程
time.sleep(5)
is_running = False

print("等待线程执行完成...")
thread.join()

print("线程执行完成!")

在这个例子中,我们创建了一个名为 count_numbers 的函数,该函数会不断地打印数字,并通过一个全局变量 is_running 控制线程的执行。在主线程中,我们等待了 5 秒后将 is_running 设置为 False,从而让线程自行退出。

安全结束线程

除了设置标志位的方式外,有时候我们可能需要更加安全和可靠地结束线程。Python 中的线程并没有提供直接的方法来强制终止线程,但可以使用一些技巧来安全地结束线程,比如使用 Thread 对象的 Event

下面是一个使用 Event 来结束线程的示例:

import threading
import time

# 创建 Event 对象
stop_event = threading.Event()

def count_numbers():
    i = 1
    while not stop_event.is_set():
        print(i)
        i += 1
        time.sleep(1)

# 创建线程
thread = threading.Thread(target=count_numbers)

# 启动线程
thread.start()

# 主线程等待一段时间后设置 Event,结束线程
time.sleep(5)
stop_event.set()

print("等待线程执行完成...")
thread.join()

print("线程执行完成!")

在这个例子中,我们创建了一个 Event 对象 stop_event,线程在每次循环中检查该事件是否被设置。在主线程中,我们等待了 5 秒后设置了 stop_event,从而结束了线程的执行。

异常处理

在线程中,异常的处理也是一个重要的问题。如果线程中发生了异常而没有处理,整个线程可能会意外终止。因此,在线程中要使用 try-except 语句来捕获异常,并进行适当的处理。

下面是一个简单的例子:

import threading
import time

def task():
    try:
        # 这里是线程执行的任务
        print("任务开始...")
        time.sleep(3)
        raise Exception("人为抛出异常")
    except Exception as e:
        print(f"捕获到异常:{e}")

# 创建线程
thread = threading.Thread(target=task)

# 启动线程
thread.start()

# 主线程等待线程执行完成
thread.join()

print("线程执行完成!")

在这个例子中,线程中的任务抛出了一个异常,但由于我们在 task 函数中使用了 try-except 语句,因此异常被捕获并打印出来,而线程并没有意外终止。

使用线程池管理线程

在实际开发中,如果需要频繁地创建和销毁线程,可能会导致性能下降。为了更有效地管理线程,可以使用线程池来重用线程对象。Python 提供了 concurrent.futures 模块,其中的 ThreadPoolExecutor 类可以帮助我们轻松地管理线程池。

下面是一个使用线程池的例子:

from concurrent.futures import ThreadPoolExecutor
import time

def task(n):
    print(f"任务 {n} 开始...")
    time.sleep(2)
    print(f"任务 {n} 完成!")

# 创建线程池
with ThreadPoolExecutor(max_workers=3) as executor:
    # 提交任务给线程池执行
    for i in range(1, 6):
        executor.submit(task, i)

print("所有任务执行完成!")

在这个例子中,我们使用 ThreadPoolExecutor 创建了一个最大工作线程数为 3 的线程池。然后,我们提交了 5 个任务给线程池执行。线程池会自动管理线程的创建和销毁,以及任务的调度。

使用 threading.Thread 的子类

除了直接使用 threading.Thread 类外,我们还可以通过继承 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"{self.name} 线程开始执行...")
        time.sleep(3)
        print(f"{self.name} 线程执行完成!")

# 创建线程实例并启动
thread1 = MyThread("Thread 1")
thread2 = MyThread("Thread 2")

thread1.start()
thread2.start()

# 等待线程执行完成
thread1.join()
thread2.join()

print("所有线程执行完成!")

在这个例子中,我们定义了一个 MyThread 类,继承自 threading.Thread,并重写了 run() 方法来定义线程执行的逻辑。然后,我们创建了两个 MyThread 的实例,并启动了这两个线程。

线程同步与共享资源

在多线程编程中,经常会遇到多个线程同时访问共享资源的情况。为了避免竞争条件和数据不一致的问题,需要使用线程同步机制来保护共享资源。

使用锁(Lock)

锁是最常见的线程同步机制之一,Python 中的 threading.Lock 类可以用来创建锁对象。在访问共享资源之前,线程可以通过调用 acquire() 方法获取锁,访问完成后再调用 release() 方法释放锁。

下面是一个使用锁来保护共享资源的例子:

import threading

shared_resource = 0
lock = threading.Lock()

def update_shared_resource():
    global shared_resource
    for _ in range(100000):
        lock.acquire()
        shared_resource += 1
        lock.release()

# 创建多个线程来更新共享资源
threads = []
for _ in range(5):
    t = threading.Thread(target=update_shared_resource)
    threads.append(t)
    t.start()

# 等待所有线程执行完成
for t in threads:
    t.join()

print("共享资源的值为:", shared_resource)

在这个例子中,我们创建了一个名为 shared_resource 的共享变量,然后使用 threading.Lock 创建了一个锁对象 lock。在 update_shared_resource 函数中,我们使用锁来保护对 shared_resource 的访问,从而避免了多个线程同时修改共享资源的问题。

使用条件变量(Condition)

条件变量是另一种常见的线程同步机制,Python 中的 threading.Condition 类可以用来创建条件变量对象。条件变量通常与锁结合使用,可以在满足特定条件时通知等待的线程。

下面是一个使用条件变量来实现生产者-消费者模式的例子:

import threading
import time

MAX_ITEMS = 5
items = []
condition = threading.Condition()

def producer():
    for i in range(10):
        time.sleep(1)
        with condition:
            if len(items) >= MAX_ITEMS:
                print("仓库已满,生产者等待...")
                condition.wait()
            print("生产者生产一个商品")
            items.append(i)
            condition.notify()

def consumer():
    for i in range(10):
        time.sleep(1.5)
        with condition:
            while not items:
                print("仓库为空,消费者等待...")
                condition.wait()
            item = items.pop(0)
            print(f"消费者消费了商品 {item}")
            condition.notify()

# 创建生产者和消费者线程并启动
producer_thread = threading.Thread(target=producer)
consumer_thread = threading.Thread(target=consumer)

producer_thread.start()
consumer_thread.start()

# 等待线程执行完成
producer_thread.join()
consumer_thread.join()

print("所有商品已被生产和消费完毕!")

在这个例子中,我们使用了条件变量 condition 来实现生产者-消费者模式。生产者线程在仓库满时等待,消费者线程在仓库空时等待,并在生产或消费完成后通过 notify() 方法通知等待的线程。

使用队列实现线程间通信

除了使用锁和条件变量等同步机制外,还可以使用队列来实现线程间的安全通信。Python 中的 queue.Queue 类提供了线程安全的队列实现,可以在多个线程之间安全地传递数据。

下面是一个使用队列实现生产者-消费者模式的例子:

import threading
import queue
import time

MAX_ITEMS = 5
queue = queue.Queue(MAX_ITEMS)

def producer():
    for i in range(10):
        time.sleep(1)
        try:
            queue.put(i, block=True, timeout=1)
            print("生产者生产一个商品")
        except queue.Full:
            print("仓库已满,生产者等待...")

def consumer():
    for i in range(10):
        time.sleep(1.5)
        try:
            item = queue.get(block=True, timeout=1)
            print(f"消费者消费了商品 {item}")
        except queue.Empty:
            print("仓库为空,消费者等待...")

# 创建生产者和消费者线程并启动
producer_thread = threading.Thread(target=producer)
consumer_thread = threading.Thread(target=consumer)

producer_thread.start()
consumer_thread.start()

# 等待线程执行完成
producer_thread.join()
consumer_thread.join()

print("所有商品已被生产和消费完毕!")

在这个例子中,我们使用了 queue.Queue 类来实现生产者-消费者模式。生产者线程通过 put() 方法向队列中添加商品,消费者线程通过 get() 方法从队列中取出商品。当队列已满时,生产者线程会等待;当队列为空时,消费者线程会等待。

使用队列实现线程间通信的好处在于,它提供了一种简单而安全的方式来传递数据,避免了显式的锁和条件变量的使用。

定时结束线程

有时候,我们希望线程在一定时间内执行完毕或者超时退出。Python 中可以利用定时器来实现这一功能。定时器可以在指定的时间后触发一个事件,我们可以利用这个特性来控制线程的执行时间。

下面是一个使用定时器结束线程的例子:

import threading
import time

def task():
    print("线程开始执行...")
    time.sleep(3)  # 模拟线程执行时间
    print("线程执行完成!")

# 创建线程
thread = threading.Thread(target=task)

# 启动线程
thread.start()

# 定时器,3秒后设置线程结束标志
def set_thread_finished():
    print("定时器触发,设置线程结束标志...")
    thread.finished = True

timer = threading.Timer(3, set_thread_finished)
timer.start()

# 主线程等待线程执行完成
thread.join()

print("线程执行完成!")

在这个例子中,我们创建了一个定时器 timer,在 3 秒后触发 set_thread_finished 函数,该函数设置了线程的结束标志。线程在执行时会检查结束标志,如果标志被设置,则提前退出。这样就实现了在指定时间后结束线程的功能。

使用 threading.Event 实现线程等待

除了定时器,我们还可以使用 threading.Event 来实现线程的等待和超时退出。Event 是线程间通信的一种机制,可以用来设置信号、等待信号等操作。

下面是一个使用 Event 实现线程等待的例子:

import threading
import time

# 创建 Event 对象
event = threading.Event()

def task():
    print("线程开始执行...")
    event.wait(3)  # 等待事件触发,超时时间为3秒
    if event.is_set():
        print("事件被触发,线程执行完成!")
    else:
        print("超时退出,线程执行未完成!")

# 创建线程
thread = threading.Thread(target=task)

# 启动线程
thread.start()

# 等待一段时间后设置事件
time.sleep(2)
print("等待2秒后设置事件...")
event.set()

# 主线程等待线程执行完成
thread.join()

print("线程执行完成!")

在这个例子中,线程在执行时等待事件的触发,如果在3秒内事件被设置,则线程执行完成;否则,线程会在超时后退出。这样就实现了在指定时间内结束线程的功能。

总结

在本文中,我们探讨了在 Python 中创建线程、结束线程以及线程管理的多种方法。我们从创建线程的基础开始,介绍了使用 threading 模块创建线程的方法,并展示了如何优雅地结束线程。接着,我们深入讨论了线程同步与共享资源的问题,介绍了使用锁、条件变量和队列等机制来保护共享资源、实现线程间通信的方法。然后,我们探讨了如何使用定时器和事件来实现线程的定时结束和超时退出,从而更灵活地控制线程的执行时间。

总的来说,本文全面介绍了多线程编程中的关键概念和技术,并提供了丰富的代码示例来帮助读者更好地理解和应用这些技术。通过合理地使用线程管理和同步机制,我们可以编写出高效、可靠的多线程程序,更好地利用计算资源,提高程序的性能和可维护性。希望本文对读者在 Python 多线程编程方面有所帮助。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/567671.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C++-DAY1

思维导图 有以下定义,说明哪些量可以改变哪些不可以改变? const char *p; const (char *) p; char *const p; const char* const p; char const *p; (char *) const p; char const* const p; const char *p:指针 p 所指向的内容不可改…

【C++庖丁解牛】C++11---右值引用和移动语义

🍁你好,我是 RO-BERRY 📗 致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 🎄感谢你的陪伴与支持 ,故事既有了开头,就要画上一个完美的句号,让我们一起加油 目录 1 左值引用和右值引用2 左…

是德软件89600 RFID使用笔记

文章目录 1、进入RFID软件:2、RFID软件解调设置项3、如何查看一段指令数据本文是日常工作的笔记分享。 lauch VSA(矢量频谱分析)后会出现以下界面: 当然这是因为频谱仪的输入有信号才显示如下: 否则就显示频谱仪的噪底 这里的设置过程同一般的频谱仪,比如中心频率、span…

逆向修改app就可以游戏充值到账?

hello ,大家好, 现在市场仍然流行着非常多的传奇类游戏私服或者其他类型的游戏私服,随着私服越来越多(很多并不合法),越来越多的人加入了破解,逆向修改,或者代充的队伍并从中获利。这里我给大家分享一下这些做代充的常规的做法,以及大家作为游戏服务器如何避坑做强校验…

ApiHug 的初心-ApiHug101

视频 秒懂 ApiHug -019 HOPE 🔥 H.O.P.E.: Help other people excellent 💝 是这个项目最初的初心 🤗 ApiHug {Postman|Swagger|Api...} 快↑ 准√ 省↓ 🏠 gitee github search ApiHug ApiHug 🤗 ApiHug {Post…

数据结构(学习笔记)王道

一、绪论 1.1 数据结构的基本概念 数据:是信息的载体,是描述客观事物属性的数、字符以及所有输入到计算机中并被计算机程序识别和处理的符号的集合。(计算机程序加工的原料)数据元素:数据的基本单位,由若干…

相关电路整理(工程)相关FOC电路整理

1. 基于STM32G4的FOC电机驱动学习板 1.1 防反接电路 电源正确接入时 电流从 VIN 端流向负载,经由 Q3(NMOS) 通向地(GND)。在上电瞬间,由于 MOS 管的体二极管效应,地回路通过体二极管接通。接下来,由于 Vgs…

【sping】在logback-spring.xml 获取项目名称

在日志文件中我们想根据spring.application.name 创建出的文件夹。 也不想死在XML文件中。 application.yml spring:application:name: my-demo logback-spring.xml <springProperty name"application_name" scope"context" source"spring.app…

Unity类银河恶魔城学习记录13-4 p145 Save Skill Tree源代码

Alex教程每一P的教程原代码加上我自己的理解初步理解写的注释&#xff0c;可供学习Alex教程的人参考 此代码仅为较上一P有所改变的代码 【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili GameData.cs using System.Collections; using System.Collections.Generic…

网络带宽相关

1.tcp重传率计算 watch -n 5 “cat /proc/net/snmp” 如下博客所讲 https://blog.csdn.net/michaelwoshi/article/details/121189743 2.iperf测试网络带宽 #客户端 #tcp iperf -c 服务端ip -P 4 -b 200M #udp iperf -c 服务端ip -u -P 4 -b 1000M -l 10K #服务端 iperf -s

云架构(五)BBF模式

BFF模式&#xff08;Backends for Frontends pattern&#xff09;- https://learn.microsoft.com/en-us/azure/architecture/patterns/backends-for-frontends。 创建单独的后台服务用以提供给特定的前端或者接口。当你希望避免为多个接口定制单独的后台时&#xff0c;此模…

隋总分享:Temu选品师算不算是蓝海项目?

在当今日新月异的互联网经济浪潮中&#xff0c;跨境电商正成为一股不可忽视的力量。最近&#xff0c;网红隋总对Temu选品师这一职业进行了深入介绍&#xff0c;引发了广泛关注。那么&#xff0c;Temu选品师是否真的可以视为一个蓝海项目呢?本文将对此进行一番细致的探讨。 首先…

RBA认证是什么?RBA认证的流程是怎么样的

RBA认证&#xff0c;即“责任商业联盟”认证&#xff0c;英文全称是Responsible Business Alliance。这是一个为电子行业或以电子为主要组成部分的行业及其供应链制定的社会责任审核标准。该标准旨在确保工作环境的安全、工人受到尊重并富有尊严、商业营运合乎环保性质并遵守道…

DenseDiffusion:Dense Text-to-Image Generation with Attention Modulation

1 研究目的 该文献的研究目的主要是&#xff1a; 探讨一种更为广泛的调制方法&#xff0c;通过设计多个正则化项来优化图像合成过程中的空间控制。论文的大致思想是&#xff0c;在现有的基于数据驱动的图像合成系统基础上&#xff0c;通过引入更复杂的调制策略&#xff0c;实现…

2024.4.23

1.const 修饰 *p。 p所指向地址内的值不可变&#xff0c;p指向的地址可以改变 2.const 修饰 p。 p指向的地址不可变&#xff0c;p所指向的地址的值可变 3.const 修饰 p。 p指向的地址不可变&#xff0c;p所指向地址的值可变 4.const 修饰 *p 和p。p指向的地址和p所指向地址的…

[激光原理与应用-90]:光功率计基本原理

目录 一、光功率计原理 二、光功率计硬件电路 三、光功率计探头 四、接口信号 一、光功率计原理 光功率计是用来测量光功率的仪器&#xff0c;其原理基于光电效应和电信号的检测与处理。 下面是光功率计的基本原理&#xff1a; 光电效应&#xff1a; 光功率计使用光敏元件…

链表的分割

题目 现给定一链表的头指针 phead 以及值 x&#xff0c;需编写一段代码将所有小于 x 节点的排在其余节点之前&#xff0c;且不能改变原来的数据顺序&#xff0c;最后返回重新排列后的链表的头指针。 算法思想 将小于x的尾插在第一个链表 将大于等于x的尾插在第二个链表 最后…

1142 - SELECT command denied to user ···

MySql子账户操作数据库权限不够&#xff0c;提示错误 1142 - SELECT command denied to user database 1142 - ALTER command denied to user database 以下命令可以解决 GRANT SELEC your_database_name TO mysql_account%;

centos7上搭建mongodb数据库

1.添加MongoDB的YUM仓库&#xff1a; 打开终端&#xff0c;执行以下命令来添加MongoDB的YUM仓库&#xff1a; sudo vi /etc/yum.repos.d/mongodb-org-4.4.repo 在打开的文件中&#xff0c;输入以下内容&#xff1a; [mongodb-org-4.4] nameMongoDB Repository baseurlh…

【Mysql】用frm和ibd文件恢复mysql表数据

问题 总是遇到mysql服务意外断开之后导致mysql服务无法正常运行的情况&#xff0c;使用Navicat工具查看能够看到里面的库和表&#xff0c;但是无法获取数据记录&#xff0c;提示数据表不存在。 这里记录一下用frm文件和ibd文件手动恢复数据表的过程。 思路 1、frm文件&…