首页 > 解决方案 > Python:socket.rcvfrom() 锁定所有脚本

问题描述

我在 python 3 中有一个使用 schedule 和 socket 的简单代码:

import schedule
import socket
from time import sleep

def readDataFromFile():
    data = []
    with open("/tmp/tmp.txt", "r") as f:
        for singleLine in f.readlines():
            data.append(str(singleLine))

    if(len(data)>0):
        writeToBuffer(data)

def readDataFromUDP():
    udpData = []
    rcvData, addr = sock.recvfrom(256)
    udpData.append(rcvData.decode('ascii'))

    if(len(udpData)>0):
        writeToBuffer(udpData)

.
.
.

def main():
    schedule.every().second.do(readDataFromFile)
    schedule.every().second.do(readDataFromUDP)

    while(1):
        schedule.run_pending()
        sleep(1)



UDP_IP = "192.xxx.xxx.xxx"
UDP_PORT = xxxx

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind((UDP_IP, UDP_PORT))

main()

问题是,脚本挂在 sock.rcvfrom() 指令上,等待数据到来。如何强制 python 独立运行这项工作?更好的主意是在线程中运行它?

标签: pythonpython-3.x

解决方案


可以在此处使用线程,它会正常工作,但需要进行一些更改。首先,后台线程上的调度程序将尝试recvfrom每秒启动一个新线程,无论最后一个线程花费了多长时间。其次,由于两个线程显然都在尝试调用相同的writeToBuffer函数,因此您可能需要 aLock或其他东西来同步它们。

围绕异步事件循环重写整个程序在这里几乎肯定是矫枉过正。

只需将套接字更改为非阻塞并进行混合可能是最简单的更改,例如,通过使用settimeout

# wherever you create your socket
sock.settimeout(0.8)

# ...

def readDataFromUDP():
    udpData = []
    try:
        rcvData, addr = sock.recvfrom(256)
    except socket.timeout:
        return
    udpData.append(rcvData.decode('ascii'))

    if(len(udpData)>0):
        writeToBuffer(udpData)

现在,每次调用时recvfrom,如果有可用数据,您将立即处理;如果没有,它将等待最多 0.8 秒,然后引发异常,这意味着您没有要处理的数据,所以返回并等待下一个循环。(这个 0.8 并没有什么神奇之处;我只是认为少于 1 秒的时间会是个好主意,所以在下一个计划时间到来之前还有时间做所有其他工作。)

Under the covers, this works by setting the OS-level socket to non-blocking mode and doing some implementation-specific thing to wait with a timeout. You could do the same yourself by using setblocking(False) and using the select or selectors module to wait up to 0.8 seconds for the socket to be ready, but it's easier to just let Python take care of that for you.


推荐阅读