Alo*_*lon 5 python sockets multithreading thread-safety
我正在编写一个 Python 程序,在主线程中,我使用 recv 函数通过 TCP 套接字连续(在循环中)接收数据。在回调函数中,我使用 sendall 函数通过同一个套接字发送数据。什么触发回调无关紧要。我已将套接字设置为阻塞。
我的问题是,这样做安全吗?我的理解是在单独的线程(不是主线程)上调用回调函数。Python 套接字对象线程安全吗?从我的研究中,我得到了相互矛盾的答案。
Bha*_*rel 10
Python 中的套接字不是线程安全的。
您试图同时解决几个问题:
您可以通过使用asyncio或以 asyncio 在内部解决问题的方式解决这些问题:通过select.select与 a 一起使用socketpair,并使用传入数据的队列。
import select
import socket
import queue
# Any data received by this queue will be sent
send_queue = queue.Queue()
# Any data sent to ssock shows up on rsock
rsock, ssock = socket.socketpair()
main_socket = socket.socket()
# Create the connection with main_socket, fill this up with your code
# Your callback thread
def different_thread():
# Put the data to send inside the queue
send_queue.put(data)
# Trigger the main thread by sending data to ssock which goes to rsock
ssock.send(b"\x00")
# Run the callback thread
while True:
# When either main_socket has data or rsock has data, select.select will return
rlist, _, _ = select.select([main_socket, rsock], [], [])
for ready_socket in rlist:
if ready_socket is main_socket:
data = main_socket.recv(1024)
# Do stuff with data, fill this up with your code
else:
# Ready_socket is rsock
rsock.recv(1) # Dump the ready mark
# Send the data.
main_socket.sendall(send_queue.get())
Run Code Online (Sandbox Code Playgroud)
我们在这里使用多个构造。您必须用您选择的代码填充空白区域。至于解释:
我们首先创建一个send_queue要发送的数据队列。然后,我们创建一对连接的套接字 ( socketpair())。稍后我们需要这个来唤醒主线程,因为我们不希望recv()阻塞和阻止写入套接字。
然后,我们连接main_socket并启动回调线程。现在神奇之处在于:
在主线程中,我们使用或select.select来了解是否有任何数据。如果其中之一有数据,则主线程被唤醒。rsockmain_socket
将数据添加到队列后,我们通过发出信号来唤醒主线程,主线程ssock被唤醒rsock并从select.select.
为了充分理解这一点,您必须阅读select.select()、socketpair()和queue.Queue()。
@tobias.mcnulty 在评论中提出了一个很好的问题:为什么我们应该使用 aQueue而不是通过套接字发送所有数据?
您也可以使用socketpair来发送数据,这有其好处,但由于多种原因,通过队列发送可能更可取:
Queue保证,我们将只有 1 次调用(对于单字节信号),而不是更多(除了队列的内部锁,但这一个非常便宜)。通过发送大量数据socketpair将导致多个系统调用。作为提示,您也可以使用collections.dequeCPython 由于 GIL 而保证线程安全的 CPython。这样,除了socketpair.socketpair.recv将通过多个调用。selectTCP 没有消息边界。您要么必须创建人工套接字,将套接字设置为非阻塞并处理异步套接字,要么将其视为流并连续传递select调用,这可能会很昂贵,具体取决于您的操作系统。总而言之,在内核和用户空间之间来回传输数据是可能的并且可行,但我个人不推荐这样做。
| 归档时间: |
|
| 查看次数: |
7061 次 |
| 最近记录: |