【问题标题】:Very Slow Binary File Download in Multithreaded Python Program on Raspberry PiRaspberry Pi 上的多线程 Python 程序中非常慢的二进制文件下载
【发布时间】:2021-03-18 23:33:22
【问题描述】:

我正在 Raspberry Pi 4 上编写一个多线程 Python 应用程序,它偶尔需要从服务器下载大约 200 KB 的二进制文件,在测试期间,该服务器是我在本地网络上的笔记本电脑。我已经验证了这些文件是由我的笔记本电脑使用 Curl 或 Python 3 CLI requests.get 调用 RPi 在大约一秒钟内提供的,但在我的应用程序中,请求下载调用在完成前至少挂起 2 分钟。受影响的代码在这里:

# requests current composite from server 
# args:     timestamp: timestamp of last update
# returns:  SUCCESS_RETURN if updated, NONE_RETURN otherwise, OFFLINE_RETURN on failure to connect

def getcomposite(self, timestamp=None):
    try:
        self.slplogger.info("Downloading composite for timestamp %s" % (dt.utcfromtimestamp(timestamp).strftime("%Y-%m-%d-%H:%M:%S") if timestamp else "None"))

        # HANGS HERE
        compositeresp = requests.post(SERVER_URL + "getcomposite", data={'mac' : self.mac, 'timestamp' : timestamp})

        self.slplogger.info("Downloaded new composite: %s" % str(compositeresp.text[:min(10, len(compositeresp.text))]))

        if compositeresp.text != NONE_RETURN and compositeresp.content:
            with self.compositelock:
                self.compositedata = np.load(BytesIO(compositeresp.content), allow_pickle=False)
                # compute new input norm for adding subsequent input
                self.compositenorm = np.mean(self.compositedata[:]['value'], dtype=int)
                self.emptycomposite = False
                self.slplogger.info("Set new composite: %s" % str(self.compositedata[:min(10, len(self.compositedata))]))
            return SUCCESS_RETURN
        return FAILURE_RETURN
    except requests.exceptions.ConnectionError:

        self.slplogger.info("Composite download failed. Unable to connect to server")
        return OFFLINE_RETURN

这里定义了调用该方法的非守护线程(COMPOSITE_POLL_INTERVAL为2秒):

# --------------------------------------------------------------------
#   CompositePollingThread - Thread superclass that periodically 
#   polls strangeloop server for new additions to the composite loop
# --------------------------------------------------------------------

class CompositePollingThread(Thread):

    # overloaded Thread constructor
    # args:     pedal: parent Pedal object that instantiated this thread

    def __init__(self, pedal):
        Thread.__init__(self)
        self.stop = Event()
        self.pedal = pedal
        self.timestamp = None

        self.pedal.slplogger.debug("Initialized composite polling thread")

    # main thread execution loop

    def run(self):

        self.pedal.slplogger.debug("Started composite polling thread")

        while self.pedal.running:
            time.sleep(COMPOSITE_POLL_INTERVAL)

            # timestamp to determine whether any new data needs to be downloaded
            if not self.pedal.recording and self.pedal.getcomposite(timestamp=self.timestamp) == SUCCESS_RETURN:
                self.timestamp = dt.utcnow().timestamp()

                self.pedal.slplogger.debug("Downloaded new composite at %s" % dt.utcfromtimestamp(self.timestamp).strftime("%Y-%m-%d-%H:%M:%S"))

        self.pedal.slplogger.debug("Ended composite polling thread")

我假设下载缓慢是由程序中的线程问题引起的。它还负责处理占用大部分 CPU 的实时输入。我能做些什么来提高这个下载速度吗?有没有办法给线程更高的优先级,或者我应该切换到多处理模块以利用 RPi 4 的多核?

【问题讨论】:

  • 您是否尝试过在没有线程的单独程序中运行相同的requests.post(...) 调用?也许requests 做了一些服务器不喜欢的事情,不管线程如何。
  • 不在单独的程序中,但我在 Python CLI 中运行了 requests.post 行,不到一秒就完成了。我还验证了 requests.get 和 requests.post 之间没有性能差异,尽管后者不常用于大型下载。

标签: python multithreading python-requests raspberry-pi4


【解决方案1】:

最后,问题出在服务器上。我将端点响应从“flask.send_file”更改为我需要的数据周围的 BytesIO 包装器到一个简单的 flask.Response 对象,其中数据作为文本(类似于Serve image stored in SQLAlchemy LargeBinary column),现在它下载不到十秒。我不知道为什么只有在通过程序访问(而不是 CLI 请求)时才需要这么长时间才能下载,但是这个更改解决了它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-08-31
    • 2019-03-21
    • 2015-10-25
    • 2017-11-28
    • 1970-01-01
    • 2023-01-19
    • 2020-05-02
    • 2021-06-23
    相关资源
    最近更新 更多