1

在python多线程中,只有主线程能接收到信号,而且不能使用join阻塞,
那么主线程接收到信号后怎么结束正在阻塞状态中的子线程呢

如下代码子线程receive_task1正阻塞在os.read()(接收串口数据,串口没有收到数据)函数中,有没有什么办法让os.read()函数退出呢?

如果在主线程中使用os.read(),默认情况可以使用Ctrl+C打断,现在这种情况如何做最佳?

import os
import signal
import threading
import time

def test(signum, frame):
    print("\nreceived sig %d" % (signum))
    print("how to make os.read(...) in rcv_task1 thread stop? ")
    print("except use sys.exit(1)")

def receive_task1():
    fd = os.open("/dev/ttyUSB1", os.O_RDWR)
    os.read(fd, 10)
    print("receive thread end")

signal.signal(signal.SIGINT, test)

rcv_task1 = threading.Thread(target=receive_task1)
rcv_task1.setDaemon(True)
rcv_task1.start()
while(1):
    time.sleep(1)
2018-11-14 提问
2 个回答
1

我只学过socket库,不熟悉os库。以我目前掌握的知识,解决办法有两个:
1、改成多进程模式,然后在主进程中terminate掉子进程即可;
2、将子线程中的文件描述符fd设置成非阻塞模式,正好查到os库中有os.set_blocking(fd, blocking)函数可供使用。
代码大致如下(我没在Linux下测试过):

import os
import signal
import threading
import time

keep_running = True

def my_handler(signum, frame):
    """定义信号处理函数"""
    global keep_running
    print('received sig {}'.format(signum))
    keep_running = False #控制子线程的关闭
    print('thre thread end')

def task():
    """被子线程调用的函数"""
    fd = os.open('example.txt', os.O_RDWR) #以可读写的方式打开指定文件
    os.set_blocking(fd, blocking=False) #将fd设置为非阻塞
    while keep_running: #在循环中不断调用非阻塞代码,跳出循环==退出子线程
        recv = os.read(fd, 10)
        time.sleep(0.1)

signal.signal(signal.SIGINT, my_handler) #注册信号处理函数,CTRL+C触发信号
thre = threading.Thread(target=task)
thre.start()
while True:
    time.sleep(1)
0

首先我没有认真看你的代码(因为我打算给你提供正确的思路)。
然后,这里是这个问题的答案:

  1. 信号与线程的关系:

    Pyhon Docs:

    18.8.1.2. Signals and threads

    Python signal handlers are always executed in the main Python thread, even if the signal was received in another thread. This means that signals can’t be used as a means of inter-thread communication. You can use the synchronization primitives from the threading module instead.
    Besides, only the main thread is allowed to set a new signal handler.
    (简单一句话,信号只在主线程中处理)

  2. 收到了信号如何退出子线程?
    子线程的状态只能子进程自己控制。 其它线程对它只有两个可用操作(.join.is_alive)。
    但是线程间是可以通信的(调度)。通过 queue.Queue 子线程就能够收到其它线程期望它的行为。
    比如主线程收到 Ctrl+C(SIGINT) 后,放置 'quit' 消息在 QUEUE 中,然后子线程从 QUEUE 获取到了 'quit' 消息后,就要自己做退出动作。
    所以为了实现这一点,你需要非常小心地编写线程(如果你的线程有阻塞的可能性并且你想要线程一直在后台运行):

    • 你需要实现线程的退出机制!
    • 你需要确保线程不会阻塞(不然它就不知道自己应该退出了)!
    • 然后再后台线程的一个特定点轮询判断是否需要退出。

如果你需要实现一个一直在后台运行的线程,那么你就不应该使用函数的方式编写线程!所以我的建议是编写一个类级的线程。这样的话你就可以将退出机制和循环机制分离出来了。
由于上述第一点的原因,所以在编写可能会阻塞的类级线程上,没有“通用”的解决方法:socket 有设置 socket 的不阻塞方式,queue.Queue 有自己的设置不阻塞的方式。
(关于什么是阻塞IO,什么是非阻塞 IO,可以看 APUE 有一章涉及。)

最后我提供我的一篇 CSDN 博客:【Python】【多线程】Python 多线程开发指南|完成度:20%
截至 2019/02/27 该文章完成 20%,但是上述所有内容都能够在该博客中找到。

撰写答案

推广链接