Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/341.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python Flask socketio在后台线程中复制文件时错过事件_Python_Multithreading_Multiprocessing_Eventlet_Flask Socketio - Fatal编程技术网

Python Flask socketio在后台线程中复制文件时错过事件

Python Flask socketio在后台线程中复制文件时错过事件,python,multithreading,multiprocessing,eventlet,flask-socketio,Python,Multithreading,Multiprocessing,Eventlet,Flask Socketio,(在github上完成测试应用程序:) 我正在使用Flask和Flask SocketIO插件。我的客户端可以要求服务器通过websocket复制文件,但在文件复制时,我希望客户端能够与服务器通信,要求服务器执行其他操作。我的解决方案是在后台线程中运行复制进程(shutil)。这就是功能: def copy_large_file(): source = "/home/christophe/Desktop/largefile" destination = "/home/christ

(在github上完成测试应用程序:)

我正在使用Flask和Flask SocketIO插件。我的客户端可以要求服务器通过websocket复制文件,但在文件复制时,我希望客户端能够与服务器通信,要求服务器执行其他操作。我的解决方案是在后台线程中运行复制进程(shutil)。这就是功能:

def copy_large_file():
    source = "/home/christophe/Desktop/largefile"
    destination = "/home/christophe/Desktop/largefile2"
    try:
        os.remove(destination)
    except:
        pass
    print("Before copy")
    socketio.emit('my_response',
                  {'data': 'Thread says: before'}, namespace='/test')
    shutil.copy(source, destination)
    print("After copy")
    socketio.emit('my_response',
                  {'data': 'Thread says: after'}, namespace='/test')
我观察到以下行为: 使用本机socketio方法启动函数时:

socketio.start_background_task(target=copy_large_file)
复制大文件时,所有传入事件都会延迟,直到文件完成并启动下一个文件。I gues shutil没有放松GIL或类似的东西,因此我测试了线程:

thread = threading.Thread(target=copy_large_file)
thread.start()
同样的行为。也许是多重处理

thread = multiprocessing.Process(target=copy_large_file)
thread.start()
啊!!在copy_large_file函数中通过socketio发出的信号正确接收。 但是: 如果用户开始复制一个非常大的文件,关闭浏览器并在2分钟后返回,套接字将不再连接到同一个socketio“会话”,因此不再接收后台进程发出的消息

我想主要的问题是:如何在后台复制大文件而不阻塞socketio,但仍然能够从后台进程向客户端发送信号

测试应用程序可用于再现以下行为:

  • 克隆
  • 安装要求
  • 在copy_file函数中选择方法(第42行)
  • 从./app.py开始
在浏览器中:

  • 转到本地主机:5000
  • 单击复制文件
  • 在复制文件时,单击Ping发送消息
  • 还要注意来自后台线程的其他信号

你在问两个不同的问题

首先,让我们讨论文件的实际复制

看起来您正在为服务器使用eventlet。虽然此框架为网络I/O功能提供了异步替换,但磁盘I/O以非阻塞方式进行要复杂得多,特别是在Linux上(关于此问题的一些信息)。所以,正如您所注意到的,即使使用标准的库monkey补丁,对文件进行I/O操作也会导致阻塞。顺便说一下,gevent也是这样

对文件执行非阻塞I/O的典型解决方案是使用线程池。有了eventlet,函数就可以做到这一点。因此,基本上,您不会直接调用
copy\u large\u file()
,而是调用
tpool.execute(copy\u large\u file)
。这将使应用程序中的其他绿色线程能够在另一个系统线程中进行复制时运行。顺便说一句,您使用另一个进程的解决方案也是有效的,但根据您需要执行其中一个副本的次数和频率,这可能会有些过分

第二个问题与“记住”启动长文件副本的客户端有关,即使浏览器已关闭并重新打开

这确实是您的应用程序需要通过存储恢复返回的客户端所需的状态来处理的事情。您的客户机可能有一种方法来识别您的应用程序,可以使用令牌或其他标识。当服务器启动其中一个文件副本时,它可以为操作分配一个id,并将该id存储在与请求该id的客户端关联的数据库中。如果客户端离开然后返回,您可以找到是否有任何正在进行的文件副本,这样可以将客户端同步回关闭浏览器之前的状态


希望这有帮助

将房间id分配给客户机,然后向房间发送消息怎么样。当客户回来时,加入前一个房间。回答得很好。注意:您只能将实际的阻塞调用放入tpool
eventlet.tpool.execute(os.remove,path)
shutil.copy
@temoto是的,这也是一个选项。@Miguel。嗯,我尝试了tpool解决方案,但得到了相同的结果。整个应用程序被阻止。文档甚至指出:函数将在池中的一个随机线程中运行,而调用的协程在其完成时阻塞。@Miguel:关于从多处理进程发送消息,我也很不走运。复制大文件功能中的消息应该由所有连接的客户端接收。我以为同一名称空间中的每个人都会收到消息。我甚至尝试使用一个名为“Everyone”的房间,并将每个连接的客户端添加到其中,但似乎函数中发送的所有内容都丢失了。@Miguel。解决了的!实际上,这是你和temoto的共同点。使用本机socketio start_background_任务加上copy_large_file函数中的一个tpool,一切正常!我更新了github项目以反映解决方案。谢谢。