简体   繁体   English

通过命名管道将数据发送到另一个已经运行的 python 进程

[英]Send data to another already running python process via named pipe

I am trying to find ways to send data to a running Python (3.7+) process via a named pipe.我试图找到通过命名管道将数据发送到正在运行的 Python (3.7+) 进程的方法。 As an example the process is adding an enthusiastic prefix to a string and printing it.例如,该过程是向字符串添加热情前缀并打印它。 The process has its own things to do and runs indefinitely.这个过程有它自己的事情要做并且无限期地运行。 Beside it own things it gets new taks.除了它自己的东西,它还有新的任务。 As there are multiple tasks ahead is had a queue:由于前面有多个任务,所以有一个队列:

from queue import Queue 
import time
import tkinter as tk 
import os
import asyncio
import win32pipe, win32file, pywintypes
import sys

q = Queue()

for i in range(5):
    q.put(i) #own tasks

class C_Window():
    def __init__(self, parent=None, windowname='Window'): 
        self.parent = parent
        self.root =  tk.Tk()
        self.sendBox = tk.Text(self.root, height=1, width=30)
        self.sendBox.pack()
        self.buttonCommit=tk.Button(self.root, height=1, width=10, text="Commit", 
                            command=lambda: self.commit_button())
        self.buttonCommit.pack()
        self.root.update()

    def commit_button(self):
        inputValue=self.sendBox.get("1.0","end-1c")
        self.sendBox.delete('1.0', tk.END)
        q.put(inputValue)

    async def async_tk(self):
        while True:
            self.root.update()
            await asyncio.sleep(0.25) 

async def async_main():
    while True:
        if q.empty():
            print ("relaxing")
        else: 
            print ("YEAY! ", q.get())
        await asyncio.sleep(1) 


if __name__ == '__main__':
    window_obj = C_Window()
    windowtask = asyncio.ensure_future(window_obj.async_tk()) # create_task() replaces ensure_future() in 3.7+ 
    maintask = asyncio.ensure_future(async_main()) 
    loop = asyncio.get_event_loop()
    loop.run_forever()

This works as expected.: Te queue get done, when something is added thorugh the interface it gets done and if nothing is in the queue it is relaxing.这按预期工作。:Te 队列完成,当通过界面添加某些内容时,它会完成,如果队列中没有任何内容,它就会放松。

Now i would like to add taks from outside to the queue via a named pipe.现在我想通过命名管道将外部任务添加到队列中。 For that i made a pipe class:为此,我做了一个管道类:

class C_Pipe():
    def __init__(self): 
        self.pipe = win32pipe.CreateNamedPipe(r'\\.\pipe\mypipe',
                                    win32pipe.PIPE_ACCESS_DUPLEX,
                                    win32pipe.PIPE_TYPE_MESSAGE | win32pipe.PIPE_READMODE_MESSAGE | win32pipe.PIPE_WAIT,
                                    1,  # nMaxInstances
                                    65536,  # nOutBufferSize
                                    65536,  # nInBufferSize
                                    0, # 50ms timeout (the default)
                                    None) # securityAttributes

    async def async_pipe(self):
        win32pipe.ConnectNamedPipe(self.pipe)
        while True:
            try:
                msg = win32file.ReadFile(self.pipe, 65536)[1].decode('utf-8')
                print(msg)
                self.main_queue.put(msg)
            except pywintypes.error as e:
                if e.winerror == 109: #no process on other side OR Pipe closed
                    win32pipe.DisconnectNamedPipe(self.pipe)
                    print("Reconnecting pipe")
                    win32pipe.ConnectNamedPipe(self.pipe)
            else:
                raise
            await asyncio.sleep(0.25)   

and then try to run it with:然后尝试使用以下命令运行它:

if __name__ == '__main__':
    window_obj = C_Window()
    windowtask = asyncio.ensure_future(window_obj.async_tk()) 
    maintask = asyncio.ensure_future(async_main()) 
    pipe_obj = C_Pipe()
    pipetask = asyncio.ensure_future(pipe_obj.async_pipe()) 
    loop = asyncio.get_event_loop()
    loop.run_forever()

Which doesn't work.哪个不起作用。 As soon as it's the pipetasks turn it blocks while reading and everything freezes.一旦管道任务转向它,它就会在阅读时阻塞并且一切都冻结。 That's why I tried to put it in a separat thread:这就是为什么我试图把它放在一个单独的线程中:

if __name__ == '__main__':

    loop2 = asyncio.new_event_loop()
    pipe_obj = C_Pipe()
    pipetask = asyncio.run_coroutine_threadsafe(pipe_obj.async_pipe(),loop2) 

    loop = asyncio.get_event_loop()
    window_obj = C_Window()
    windowtask = asyncio.ensure_future(window_obj.async_tk()) 
    maintask = asyncio.ensure_future(async_main()) 

    loop.run_forever()

but the pipe recieves 1 message and then fails without every putting data into the Queue.但是管道收到 1 条消息,然后在没有将数据放入队列的情况下失败。 My questions are:我的问题是:

  1. Is there a way to out data into a Queue from an outside process (and get rid of the named pipe)?有没有办法从外部进程将数据输出到队列中(并摆脱命名管道)?
  2. Is there a way to run the named pipe in its own thread so it can stay blocked?有没有办法在自己的线程中运行命名管道,以便它可以保持阻塞?
  3. Is asyncio really the right choice here? asyncio 真的是这里的正确选择吗? I have been reading a lot about asyncio and multiprocessing but I cant find a clear picture我已经阅读了很多关于asynciomultiprocessing但我找不到清晰的图片

Thank you very much!非常感谢!

  1. Is there a way to run the named pipe in its own thread so it can stay blocked?有没有办法在自己的线程中运行命名管道,以便它可以保持阻塞?

This is probably the easiest approach.这可能是最简单的方法。 Your attempt failed because you never actually created a different thread.您的尝试失败了,因为您实际上从未创建过不同的线程。 You created two event loops and ran only one.您创建了两个事件循环并且只运行了一个。 The idea with run_coroutine_threadsafe is to allow non-asyncio threads to submit jobs to the (single) asyncio event loop. run_coroutine_threadsafe的想法是允许非 asyncio 线程将作业提交到(单个)asyncio 事件循环。 First, the communication relies on sync APIs, so it can remain sync:首先,通信依赖于同步 API,因此它可以保持同步:

    # calling it sync_pipe, since it's no longer async
    def sync_pipe(self, enqueue):
        win32pipe.ConnectNamedPipe(self.pipe)
        while True:
            try:
                msg = win32file.ReadFile(self.pipe, 65536)[1].decode('utf-8')
                print(msg)
                enqueue(msg)
            except pywintypes.error as e:
                if e.winerror == 109: #no process on other side OR Pipe closed
                    win32pipe.DisconnectNamedPipe(self.pipe)
                    print("Reconnecting pipe")
                    win32pipe.ConnectNamedPipe(self.pipe)
            else:
                raise
            time.sleep(0.25)   

Note how we've sent it an abstract function that defines how to enqueue something.请注意我们如何向它发送一个抽象函数,该函数定义了如何将某些内容入队。 With that in place the main block can look like this:有了它,主块看起来像这样:

if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    queue = asyncio.Queue()
    def enqueue(item):
        loop.call_soon_threadsafe(queue.put_nowait, item)
    pipe_obj = C_Pipe()
    pipethread = threading.Thread(target=pipe_obj.sync_pipe, args=(enqueue,))

    window_obj = C_Window()
    windowtask = asyncio.ensure_future(window_obj.async_tk()) 
    maintask = asyncio.ensure_future(async_main(queue)) 

    loop.run_forever()

async_main now receives the queue that it will share with sync_pipe . async_main现在接收它将与sync_pipe共享的队列。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM