You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Python中安全终止运行阻塞Socket函数的线程?

解决阻塞Socket线程优雅终止与无错关闭问题

方法1:捕获预期异常并优雅退出

当Socket被关闭时,阻塞的accept()会抛出OSError,我们可以在线程函数中捕获该异常,判断是Socket关闭导致的错误后直接退出循环,避免错误信息输出。

修改后的代码:

import threading
import socket

def accept_clients(a_socket, stop_event):
    print("Accepting clients!")
    while not stop_event.is_set():
        try:
            conn, addr = a_socket.accept()
            print(f"A client connected from {addr}!")
            # 处理客户端连接后及时关闭
            conn.close()
        except OSError as e:
            # 兼容Windows(10038)和Linux/macOS(9)的Socket关闭错误码
            if e.errno in (10038, 9):
                break
            # 其他异常正常抛出
            else:
                raise
    print("Accept thread exited gracefully.")

my_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
my_socket.bind(("127.0.0.1", 6000))
my_socket.listen(10)

# 用事件通知线程停止
stop_event = threading.Event()
accept_thread = threading.Thread(target=accept_clients, args=(my_socket, stop_event))
accept_thread.start()

# 模拟业务场景:5秒后关闭Socket与线程
import time
time.sleep(5)

# 先标记停止,再关闭Socket
stop_event.set()
my_socket.close()
accept_thread.join()
print("Socket closed and thread terminated.")

方法2:非阻塞Socket+超时检查终止标志

将Socket设置为非阻塞模式,定期检查终止标志,避免一直阻塞在accept()上,这种方式更主动,无需依赖异常捕获。

import threading
import socket
import time

def accept_clients(a_socket, stop_event):
    print("Accepting clients!")
    # 设置Socket为非阻塞模式
    a_socket.setblocking(False)
    while not stop_event.is_set():
        try:
            conn, addr = a_socket.accept()
            print(f"A client connected from {addr}!")
            conn.close()
        except BlockingIOError:
            # 无客户端连接时短暂休眠,避免占用过多CPU
            time.sleep(0.1)
        except OSError as e:
            if e.errno not in (10038, 9):
                raise
    print("Accept thread exited gracefully.")

my_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
my_socket.bind(("127.0.0.1", 6000))
my_socket.listen(10)

stop_event = threading.Event()
accept_thread = threading.Thread(target=accept_clients, args=(my_socket, stop_event))
accept_thread.start()

# 模拟关闭场景
time.sleep(5)
stop_event.set()
my_socket.close()
accept_thread.join()
print("Socket closed and thread terminated.")

方法3:自连接唤醒阻塞的accept

通过连接自身监听Socket,唤醒阻塞的accept(),让线程从阻塞状态返回后检查终止标志并退出。

import threading
import socket

def accept_clients(a_socket, stop_event):
    print("Accepting clients!")
    while not stop_event.is_set():
        try:
            conn, addr = a_socket.accept()
            # 识别自连接,直接关闭并继续检查终止标志
            if addr[0] == "127.0.0.1":
                conn.close()
                continue
            print(f"A client connected from {addr}!")
            conn.close()
        except OSError as e:
            if e.errno in (10038, 9):
                break
            raise
    print("Accept thread exited gracefully.")

my_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
my_socket.bind(("127.0.0.1", 6000))
my_socket.listen(10)

stop_event = threading.Event()
accept_thread = threading.Thread(target=accept_clients, args=(my_socket, stop_event))
accept_thread.start()

# 模拟关闭场景
import time
time.sleep(5)

stop_event.set()
# 自连接唤醒阻塞的accept
try:
    temp_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    temp_sock.connect(("127.0.0.1", 6000))
    temp_sock.close()
except:
    pass
my_socket.close()
accept_thread.join()
print("Socket closed and thread terminated.")

关键注意事项

  • 优先使用threading.Event作为线程终止信号,Python不推荐强制终止线程。
  • 关闭Socket前先设置终止标志,给线程留出优雅退出的时间。
  • 不同操作系统的Socket错误码不同,需兼容Windows(10038)与Linux/macOS(9)的情况。

内容的提问来源于stack exchange,提问作者Ali Awan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 03:40:33