【问题标题】:Load data in background thread with Python 3使用 Python 3 在后台线程中加载数据
【发布时间】:2020-10-29 09:06:27
【问题描述】:

对于无法解决这个看似简单的问题,我有点沮丧:

我有一个函数需要一些时间来加载数据:

def import_data(id):
    time.sleep(5)
    return 'data' + str(id)

DataModel 类调用此函数并管理两个数据集。

class DataModel():
    def __init__(self):
        self._data_1 = import_data(1)
        self._data_2 = import_data(2)

    def retrieve_data_1(self):
        return self._data_1

    def retrieve_data_2(self):
        return self._data_2

现在,主 UI 创建了 DataModel,同时调用了两个 import_data 函数,这会阻止它。

def main_ui():
    # This takes 5 seconds for each dataset and blocks the main UI thread
    dm = DataModel()

    # Other stuff is happening. This time could be used to load data in the background
    time.sleep(2)

    # Retrieve the first dataset
    data_1 = dm.retrieve_data_1()

    # User interaction. This time could be used to load even larger datasets
    time.sleep(10)

    # Retrieve the second dataset
    data_2 = dm.retrieve_data_2()

我希望在后台加载数据集以减少 UI 被阻塞的时间。 我的想法是像这样 pseudocode 实现它:

class DataModel():
    def __init__(self):
        self._data_1 = Thread(import_data(1)).start()
        self._data_2 = Thread(import_data(2)).start()

    def retrieve_data_1(self):
        return self._data_1.wait_for_result()

    def retrieve_data_2(self):
        return self._data_2.wait_for_result()

import_data 函数在单独的线程中调用并返回 Future 对象。 retrieve_data 函数要么阻塞等待 Future 评估的主线程,要么立即返回其结果。

有没有简单的方法在 Python 3.x 中使用threading 和/或asyncio 实现这一点?提前致谢!

(编辑:语法更正)

【问题讨论】:

  • 试试这个Thread(target=import_data, args=(1,)).start() 仍然需要等待数据。您将需要存储线程对象并调用.join() 以查看它是否完成
  • 感谢您的回答。使用这种方法,我很难检索线程返回的数据。一些帖子建议使用队列,但我更倾向于直接调用 .result()。

标签: python python-asyncio python-multithreading


【解决方案1】:

使用专为此类用途设计的concurrent.futures 模块:

_pool = concurrent.futures.ThreadPoolExecutor()

class DataModel():
    def __init__(self):
        self._data_1 = _pool.submit(import_data, 1)
        self._data_2 = _pool.submit(import_data, 2)

    def retrieve_data_1(self):
        return self._data_1.result()

    def retrieve_data_2(self):
        return self._data_2.result()

如果您的函数是全局的,并且您的数据可序列化,您甚至可以从 ThreadPoolExecutor 无缝切换到 ProcessPoolExecutor 并受益于真正的(基于进程的)并行性。

【讨论】:

  • 这就像一个魅力,谢谢!无论如何,您是否知道 asyncio 是否提供了替代方法?这应该是一个典型的 asyncio 问题,但我找不到您提出的线程解决方案的优势。
  • 很棒的解决方案 :) 因为线程/队列/获取解决方案的问题可能是结果没有以正确的顺序出现在队列中
  • @MarcoK 谢谢!这根本不是典型的 asyncio 问题,尽管我理解为什么会这样。在较旧的 API 中,“异步”指的是在后台(在工作线程或线程池中)运行东西并在完成时调用回调。在包括 asyncio 在内的现代用法中,async 指的是全面使用非阻塞调用,并且(在 Python 中)完全避免使用线程。然后使用协程作为避免“回调地狱”的手段,但这意味着整个程序必须从上到下为async。鉴于您对问题的描述,asyncio 对您来说不是一个好的解决方案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-02-07
  • 1970-01-01
  • 2023-03-09
  • 2011-10-21
  • 2011-11-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多