【问题标题】:How to run a function in 'background'如何在“后台”中运行函数
【发布时间】:2019-02-07 18:55:38
【问题描述】:

我正在解析一个不断更新的日志文件的最后一行。如果匹配,我想将匹配返回到列表并使用该数据启动另一个函数。即使新功能继续运行,我也需要继续关注新条目并解析它们。

我已经从几个不同的角度进行了大约一周的工作,并取得了不同程度的成功。我尝试了线程,但在获取返回值时遇到了问题,我尝试使用全局 var 但无法使其正常工作。我现在正在尝试 asyncio,但要使其正常工作还有更多问题。

def tail():
    global match_list
    f.seek(0, os.SEEK_END)
    while True:
        line = f.readline()
        if not line:
            time.sleep(0.1)
            continue
        yield line

def thread():
    while True:
        tail()

def somefun(list):
    global match_list
    #do things here
    pass

def main():
    match_list = []
    f = open(r'file.txt')
    thread=threading.Thread(target=thread, args=(f,))
    thread.start()
    while True:
        if len(match_list) >= 1:
        somefun(match_list)        

if __name__ == '__main__':
    main()

以上是凭记忆写的。。 我希望 tail() 将该行返回到 somefun() 可以使用的列表。 我在让它工作时遇到问题,我将使用线程或 asyncio.. 任何东西让它在这一点上运行。

【问题讨论】:

    标签: python-3.x python-asyncio python-multithreading


    【解决方案1】:

    在 asyncio 中,您可能会使用两个协程,一个从文件读取,另一个处理文件。由于它们使用队列进行通信,因此它们不需要全局变量。例如:

    import os, asyncio
    
    async def tail(f, queue):
        f.seek(0, os.SEEK_END)
        while True:
            line = f.readline()
            if not line:
                await asyncio.sleep(0.1)
                continue
            await queue.put(line)
    
    async def consume(queue):
        lines = []
        while True:
            next_line = await queue.get()
            lines.append(next_line)
            # it is not clear if you want somefun to receive the next
            # line or *all* lines, but it's easy to do either
            somefun(next_line)
    
    def somefun(line):
        # do something with line
        print(f'line: {line!r}')
    
    async def main():
        queue = asyncio.Queue()
        with open('file.txt') as f:
            await asyncio.gather(tail(f, queue), consume(queue))
    
    if __name__ == '__main__':
        asyncio.run(main())
        # or, on Python older than 3.7:
        #asyncio.get_event_loop().run_until_complete(main())
    

    基于 asyncio 的解决方案的美妙之处在于,您可以轻松地并行启动任意数量的此类协程(例如,您可以在外部协程中启动 gather(main1(), main2()) 并运行它),并让它们共享相同的线程。

    【讨论】:

    • 将您的代码编辑为我 认为 正确的内容。在你的情况下,当换行符进入时,它不会在启动 somefun() 后考虑它。我仍然不确定 asyncio 是如何工作的,但你的例子对 @user4815162342 有很大帮助
    • @Scott 同时我已经测试了代码并修复了一些错别字,在此过程中向somefun 添加了一些输出。不过,我没有注意到它缺少换行符。当前版本还会出现这种情况吗?
    • 我会尝试解释得更好...我不确定如何正确测试它,因为 time.wait 只会冻结所有内容(我认为)。在我的主程序中, main() 中的函数大约需要一分钟+才能完成。在此期间,我需要监视是否有新消息传入。 somefun() 完成其工作后,我需要从列表中删除该行,然后继续下一个,或者继续等待新的。
    • @Scott 在这个版本中,您甚至可能不需要从“列表”中删除,因为队列服务于这个目的。我保留了一个列表,以防您出于其他原因需要将其发送到 somefun - 例如,如果 somefun 出于任何原因需要访问所有以前的消息。
    • 我将如何主动检查队列,以在 somefun 启动后更改其行为?somefun 需要从 tail 的结果中更新以决定如何继续。
    【解决方案2】:

    通过一些小修复,您几乎可以运行这个 :)(内部有 cmets)

    match_list  # should be at the module scope
    
    def tail():
        # f = open(...) ???
        f.seek(0, os.SEEK_END)
        while True:
            line = f.readline()
            if not line:
                time.sleep(0.1)
                continue
            yield line
    
    def thread():
        for line in tail():
            match_list.append(line)  # append line
        print("thread DONE!")
    
    def somefun(list):
        #do things here
        while match_list:
            line = match_list.pop(0)
            print(line)
    
    def main():
        match_list = []
        f = open(r'file.txt')
        thread=threading.Thread(target=thread, args=(f,))
        thread.start()
        while True:
            if match_list:
                somefun(match_list)
            time.sleep(0.1)  # <-- don't burn the CPU :)
    

    【讨论】:

      猜你喜欢
      • 2015-10-29
      • 1970-01-01
      • 2022-11-02
      • 2020-08-29
      • 2019-08-29
      • 2011-06-30
      • 2019-08-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多