Python:套接字和线程?

15

我有一个设计问题:

我有两个线程,一个心跳/控制线程和一个消息处理线程。

它们共享同一个套接字,但消息处理线程仅发送消息而不接收。心跳线程发送和接收消息(接收消息并对心跳做出反应)。

问题是我不确定这样做是否安全。我自己没有实现机制来查看套接字是否正在使用。 因此,在Python中共享套接字是否自动线程安全?

如果不安全,我将它们放在单独的线程中的原因是心跳比消息处理更重要。这意味着即使它被消息淹没,它仍然需要执行心跳操作。因此,如果必须实现插件,有没有一种方法可以使我的心跳/控制线程优先发送心跳?

4个回答

21

使用 threading.Lock() 保护套接字资源,而不是使用第三个线程,这是一种更好的方式,因为它消除了需要第三个线程的需求。相比于使用第三个线程,你会拥有更低的开销和更少的延迟。

import threading

lock = threading.Lock()

def sendfunction(sock, data):
    with lock:
        sock.send(data)

你可以从任何一个线程调用它,但是一次只允许一个线程调用sock.send。当一个线程到达已被另一个线程锁定的锁时,它将睡眠直到另一个线程释放锁,然后它将获取锁并重复这个过程。

线程模块包含LockRLockCondition,在处理多个线程时非常有用,熟悉它们及其用法值得花费一些时间。

你可以通过在处理每条消息之前检查当前时间与上次发送心跳的时间来将心跳合并到消息处理中,并且这可以防止由于消息泛滥而未能发送心跳。问题是,如果您的消息处理代码不运行,则不会发送心跳。您可以通过使消息处理代码在间隔时间内获得虚假消息来减轻此问题,以便它可以检查是否需要发送心跳并忽略虚拟消息。

尽量少使用线程(以单个线程为目标),但在您的情况下,使用一个线程可能还可以,因为它将大部分时间都在睡眠。然而,您不应该使用守护线程,因为它们不能正确地关闭。虽然在您的情况下可能没有任何损害,但如果未能正确关闭,仍可能引发某种类型的错误消息或故障。

我不赞成多路套接字方法,因为我认为这实际上会使情况更加复杂。你会发现许多类型的网络服务/应用程序将心跳和消息合并到单个套接字字节流中。


10

很遗憾,多线程共享的套接字不是线程安全的。想一想两个线程在没有锁的情况下操作的缓冲区。

正常的实现方式是使用两个套接字,就像FTP所做的那样。一个指令套接字和一个消息套接字。

如果你想通过一个套接字实现这个功能,可以将不同类型的消息放入不同的队列中,并使用第三个线程消费队列并通过唯一的套接字发送它们。

这样,你就可以控制心跳消息的优先级比数据消息高。


13
在一个线程上阅读数据并在另一个线程上发送数据是线程安全的吗? - Colateral
4
请问有人能回答@Colateral上面的问题吗?我也有同样的疑惑。 - rdp
我有同样的疑问。 - Jasha
我相信@yancl在谈论在线程之间使用队列(可能是双端队列?)。双端队列可以执行原子操作来添加值和弹出值。这意味着线程A可以生成控制消息并将它们附加到控制消息双端队列中。线程B可以生成数据消息并将其附加到数据消息双端队列中。然后,线程C将拥有实际的套接字,并从两个双端队列中弹出值并将它们发送到套接字上。在这种情况下,仍然只有3个线程中的1个套接字。然后,线程C可以优先处理来自控制双端队列的消息。 - Eric Evans
关于在两个不同的线程中读写套接字...如果在线程与套接字之间有双端队列,那么您就不必担心发送/接收代码和套接字线程之间的问题。套接字线程将接收并将这些消息推入一个双端队列,而您的读取器代码则在另一端。所有直接使用套接字进行发送/接收的操作都应该在一个线程中完成。 - Eric Evans

0

我不知道在Python层面上如何设置优先级。

因此,我建议使用2个进程而不是线程,并在操作系统级别上设置优先级。在Unix上,您可以使用os.nice()来实现。

然后,您需要使用2个套接字,同时解决您的共享问题。


0
如果两个线程都是客户端线程,最好打开两个客户端套接字,一个用于心跳到服务器,另一个用于通信。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接