当前位置: 首页 > article >正文

2025年2月2日(多任务 线程)

dw 快速删除这个单词
并行,并发

在这里插入图片描述

主线程,子线程

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这段代码展示了如何使用 Python 的 threading 模块来创建和启动一个新的线程。让我逐步解释一下这段代码:

1. 导入必要的模块

import threading
import time
  • threading 模块提供了用于创建和管理线程的功能。
  • time 模块提供了时间相关的功能,这里我们使用了 time.sleep() 来让线程暂停。

2. 定义 MyThread

class MyThread(threading.Thread):
    def run(self):
        for i in range(3):
            time.sleep(1)
            msg = "I'm " + self.name + ' @ ' + str(i)
            print(msg)
            print(self.test())
  • MyThread 继承自 threading.Thread 类,这意味着 MyThread 是一个线程类。
  • run() 方法是 Thread 类中一个重要的方法,当线程启动时,Python 会自动调用 run() 方法。
    • for i in range(3)::线程会循环 3 次,每次都进行一次 1 秒的暂停(time.sleep(1))。
    • self.name:每个线程都有一个 name 属性,表示线程的名字,默认会设置为类似 Thread-1Thread-2 这样的名字。
    • 每次打印一个包含当前线程名字和循环索引的消息:I'm Thread-1 @ 0(具体的名字会根据线程编号变化)。
    • self.test() 会调用 MyThread 类中的 test() 方法,并打印其返回值。

3. test() 方法

def test(self):
    print("nb")
  • test() 是一个简单的方法,它每次被调用时,会输出 "nb"

4. 启动线程

if __name__ == '__main__':
    t = MyThread()
    t.start()
  • if __name__ == '__main__': 确保代码仅在作为主程序执行时才会运行,而在作为模块导入时不会执行。
  • t = MyThread():创建 MyThread 类的实例(即一个线程对象)。
  • t.start():调用 start() 方法启动线程,线程一旦启动,Python 会自动调用 run() 方法。

代码执行过程:

  1. 代码执行时,会创建并启动一个新线程(t.start()),它会在 run() 方法中执行。
  2. 线程在 run() 方法内循环 3 次,每次暂停 1 秒,打印类似以下的输出:
    I'm Thread-1 @ 0
    nb
    I'm Thread-1 @ 1
    nb
    I'm Thread-1 @ 2
    nb
    
  3. 由于是线程,可能会有一些时间延迟或交替输出的现象,特别是当多个线程同时运行时。

总结

这段代码演示了如何通过继承 threading.Thread 创建自定义线程类,并重写 run() 方法来定义线程执行的任务。在每次循环中,线程会休眠 1 秒,打印当前线程的信息,并调用 test() 方法输出 "nb"

你看到输出 None 是因为 self.test() 调用了 test() 方法,但 test() 方法没有返回任何值,默认会返回 None。在 Python 中,若一个函数没有显式的 return 语句,它会默认返回 None

在你的代码中,test() 方法的定义是这样的:

def test(self):
    print("nb")

test() 方法只打印了 "nb",但并没有显式地返回任何东西。因此,当你在 run() 方法中调用 self.test() 时,print(self.test()) 会输出 test() 的返回值,也就是 None

解决方法

如果你不想看到 None,你有两种选择:

  1. 返回一个值而不是默认的 None
    修改 test() 方法,使其返回一个值(例如字符串),然后打印这个值:

    def test(self):
        print("nb")
        return "some value"  # 返回你想要的值
    

    这样修改后,self.test() 会返回 "some value"print(self.test()) 就会打印 "some value" 而不是 None

  2. 只调用 test(),不打印它的返回值
    如果你只是想调用 test() 来执行它的副作用(打印 "nb"),但不关心它的返回值,你可以直接调用它,而不需要 print(self.test())

    self.test()  # 只调用,不打印返回值
    

    这样就只会打印 "nb",而不会显示 None

总结

  • 你看到 None 是因为 test() 方法没有返回任何值,默认返回 None,然后被 print() 打印出来。
  • 你可以选择返回一个值,或者直接调用 test(),以避免输出 None

多线程共享全局变量

在这里插入图片描述
在这里插入图片描述

资源竞争

在这里插入图片描述
在这里插入图片描述
从错误信息来看,问题出在你的UDP套接字在一个线程中被关闭后,另一个线程仍然尝试使用它。这导致了 OSError: [WinError 10038] 在一个非套接字上尝试了一个操作 的错误。
以下是一些可能的解决方案和改进建议:

  1. 线程同步:在关闭套接字之前,确保所有线程都已经完成了它们的任务。你可以使用线程同步机制,比如 threading.Event 来协调线程的关闭。
  2. 修改退出条件:在 send_msgrecv_msg 函数中,使用一个共享的退出标志来控制何时退出循环,而不是直接关闭套接字。
  3. 关闭套接字的时机:在主线程中等待所有子线程完成后再关闭套接字。
    以下是一个改进后的代码示例:
import socket
import threading
def send_msg(udp_socket, stop_event):
    while not stop_event.is_set():
        send_data = input("输入:")
        if send_data == "1":
            stop_event.set()
            break
        udp_socket.sendto(send_data.encode("utf-8"), ("192.168.0.178", 8080))
def recv_msg(udp_socket, stop_event):
    while not stop_event.is_set():
        try:
            recv_data, _ = udp_socket.recvfrom(1024)
            message = recv_data.decode("utf-8")
            print(message)
            if message == "1":
                stop_event.set()
                break
        except OSError:
            break
def main():
    # 1. 创建udp
    udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    # 2. 绑定端口
    addr = ("", 7890)
    udp_socket.bind(addr)
    # 创建一个事件对象用于线程间通信
    stop_event = threading.Event()
    # 多线程
    t_send = threading.Thread(target=send_msg, args=(udp_socket, stop_event))
    t_recv = threading.Thread(target=recv_msg, args=(udp_socket, stop_event))
    t_send.start()
    t_recv.start()
    # 等待线程结束
    t_send.join()
    t_recv.join()
    # 5. 关闭套接字
    udp_socket.close()
if __name__ == "__main__":
    main()

关键改进点:

  • 使用 threading.Event 对象 stop_event 来在两个线程之间共享退出信号。
  • recv_msg 中添加了异常处理,以防止在套接字关闭后继续尝试接收数据。
  • 在主线程中使用 join() 方法等待两个子线程结束后再关闭套接字。

多线程udp聊天

import socket
import threading
import time


def send_msg(udp_socket):
    while True:
        send_data = input("输入:")
        if send_data == "1":
            print("发送结束")
            break
        udp_socket.sendto(send_data.encode("utf-8"), ("192.168.0.178", 8080))


def recv_msg(udp_socket):
    while True:
        recv_data = udp_socket.recvfrom(1024)
        print(recv_data[0].decode("utf-8"))
        if recv_data[0].decode("utf-8") == "1":
            print("接收结束")
            break



def main():
    # 1. 创建udp
    udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

    # 2. 绑定端口
    addr = ("", 7890)
    udp_socket.bind(addr)

    # 多线程
    t_send = threading.Thread(target=send_msg, args=(udp_socket,))  # 3. 发送数据
    t_recv = threading.Thread(target=recv_msg, args=(udp_socket,))  # 4 .接收数据
    t_send.start()
    t_recv.start()

    # 5. 关闭套接字
    while True:
        if len(threading.enumerate()) == 1:
            print(threading.enumerate())
            break
        time.sleep(2)
    udp_socket.close()


if __name__ == "__main__":
    main()


http://www.kler.cn/a/536853.html

相关文章:

  • 【iOS自动化】Xcode配置WebDriverAgent
  • 从 .NET Framework 升级到 .NET 8 后 SignalR 问题处理与解决方案
  • c语言对应汇编写法(以中微单片机举例)
  • Python aiortc API
  • 【教程】docker升级镜像
  • 【Kubernetes Pod间通信-第2篇】使用BGP实现Pod到Pod的通信
  • vue3 的 onScopeDispose 是什么作用
  • 【数据结构-C语言】绪论
  • 0207算法:寻找目标值、库存管理
  • 101.对称二叉树 python
  • 【现代深度学习技术】深度学习计算 | 读写文件
  • UdpServer
  • springboot基于微信小程序的仓储管理系统
  • Python——Unicode 编码 或 解码 工具(通用版)
  • PHP:动态网站开发的灵活之选
  • .net的一些知识点
  • 无法使用ip连接服务器的mysql
  • Verilog代码实例
  • 摄像头模块烟火检测
  • 【提示工程】:如何有效与大语言模型互动
  • 蓝桥杯 Java 之输入输出
  • matlab simulink 汽车四分之一模型主动被动悬架-LQR
  • 【Apache Paimon】-- 15 -- 利用 paimon-flink-action 同步 postgresql 表数据
  • MySQL数据库(五)索引1
  • 通过制作docker镜像的方式在阿里云部署前端后台服务
  • cuda手搓CNN识别手写数字