2011-10-10 24 views
24

NB. Tôi đã thấy Log output of multiprocessing.Process - thật không may, nó không trả lời câu hỏi này.Khả năng đa xử lý Python: Làm thế nào tôi có thể chuyển hướng stdout nhanh chóng từ một tiến trình con?

Tôi đang tạo quy trình con (trên cửa sổ) qua đa xử lý. Tôi muốn tất cả đầu ra stdout và stderr của quá trình con được chuyển hướng đến tệp nhật ký, thay vì xuất hiện trên bảng điều khiển. Các gợi ý duy nhất tôi đã thấy là cho quá trình con để thiết lập sys.stdout vào một tập tin. Tuy nhiên, điều này không có hiệu quả chuyển hướng tất cả các đầu ra stdout, do hành vi của chuyển hướng stdout trên Windows.

Để minh họa cho vấn đề, xây dựng Windows DLL với đoạn mã sau

#include <iostream> 

extern "C" 
{ 
    __declspec(dllexport) void writeToStdOut() 
    { 
     std::cout << "Writing to STDOUT from test DLL" << std::endl; 
    } 
} 

Sau đó tạo và chạy một kịch bản python như sau, trong đó nhập khẩu DLL này và gọi hàm:

from ctypes import * 
import sys 

print 
print "Writing to STDOUT from python, before redirect" 
print 
sys.stdout = open("stdout_redirect_log.txt", "w") 
print "Writing to STDOUT from python, after redirect" 

testdll = CDLL("Release/stdout_test.dll") 
testdll.writeToStdOut() 

Để xem hành vi tương tự như tôi, có lẽ cần thiết cho DLL được xây dựng dựa trên thời gian chạy C khác với thời gian chạy Python. Trong trường hợp của tôi, trăn được xây dựng với Visual Studio 2010, nhưng DLL của tôi được xây dựng với VS 2005.

Các hành vi tôi thấy là giao diện điều khiển cho thấy:

> stdout_test.py 

Writing to STDOUT from python, before redirect 

Writing to STDOUT from test DLL 

Trong khi các tập tin stdout_redirect_log.txt kết thúc chứa:

Writing to STDOUT from python, after redirect 

Nói cách khác, thiết lập sys.stdout không chuyển hướng đầu ra stdout do DLL tạo ra. Điều này là không đáng ngạc nhiên vì bản chất của các API cơ bản cho chuyển hướng stdout trong Windows. Tôi đã gặp phải vấn đề này ở cấp bản địa/C++ trước và không bao giờ tìm thấy một cách để chuyển hướng đáng tin cậy stdout từ bên trong một tiến trình. Nó phải được thực hiện bên ngoài. Đây thực sự là lý do tôi đang khởi chạy một tiến trình con - đó là để tôi có thể kết nối bên ngoài với các đường ống của nó và do đó đảm bảo rằng tôi đang chặn tất cả các đầu ra của nó. Tôi chắc chắn có thể làm điều này bằng cách khởi chạy quy trình thủ công với pywin32, nhưng tôi rất muốn có thể sử dụng các phương tiện đa xử lý, đặc biệt là khả năng giao tiếp với tiến trình con thông qua một đối tượng Ống đa xử lý, để có được tiến trình cập nhật. Câu hỏi đặt ra là liệu có bất kỳ cách nào để sử dụng đa xử lý cho các cơ sở IPC của mình để chuyển hướng đáng tin cậy tất cả đầu ra stdout và stderr của đứa trẻ sang một tệp.

CẬP NHẬT: Nhìn vào mã nguồn để xử lý đa .Processs, nó có một thành viên tĩnh, _Popen, có vẻ như nó có thể được sử dụng để ghi đè lên lớp được sử dụng để tạo quy trình. Nếu được đặt thành Không (mặc định), nó sử dụng một multiprocessing.forking._Popen, nhưng có vẻ như bằng cách nói

multiprocessing.Process._Popen = MyPopenClass 

Tôi có thể ghi đè quá trình tạo. Tuy nhiên, mặc dù tôi có thể lấy được điều này từ multiprocessing.forking._Popen, có vẻ như tôi sẽ phải sao chép một loạt nội dung vào trong quá trình triển khai của tôi, điều này nghe có vẻ không ổn định và không tương lai. Nếu đó là sự lựa chọn duy nhất tôi nghĩ rằng tôi có thể sẽ plump để làm toàn bộ điều bằng tay với pywin32 thay thế.

+0

Bạn có thể sử dụng API Win32 để khởi chạy tiến trình con hay không phải thực hiện bằng các thư viện Python hiện có? –

+0

Có, tôi đã đề cập trong câu hỏi rằng "Tôi chắc chắn có thể làm điều này bằng cách khởi chạy quá trình thủ công với pywin32". Nó chỉ có vẻ là một sự xấu hổ để từ bỏ mô-đun đa xử lý độc lập cấp cao hơn vì những gì có vẻ như một chút tầm thường thiếu chức năng - khả năng xác định stdin/stdout xử lý cho trẻ. – Tom

+1

Cách tiếp cận tôi đang dùng (trừ khi có ai đó đưa ra giải pháp thay thế tốt hơn) là khởi chạy quá trình thông qua mô-đun phụ, với stdin/stdout được chuyển hướng đến một tệp và sử dụng một đường dẫn Windows có tên gốc để liên lạc tiến trình. – Tom

Trả lời

7

Giải pháp bạn đề xuất là giải pháp tốt nhất: tạo quy trình của bạn theo cách thủ công để bạn có quyền truy cập rõ ràng vào các trình xử lý tệp stdout/stderr của mình. Sau đó bạn có thể tạo một socket để giao tiếp với tiến trình con và sử dụng multiprocessing.connection trên socket đó (multiprocessing.Pipe tạo cùng một kiểu đối tượng kết nối, vì vậy nó sẽ cung cấp cho bạn tất cả các chức năng IPC giống nhau).

Dưới đây là ví dụ hai tệp.

master.py:

import multiprocessing.connection 
import subprocess 
import socket 
import sys, os 

## Listen for connection from remote process (and find free port number) 
port = 10000 
while True: 
    try: 
     l = multiprocessing.connection.Listener(('localhost', int(port)), authkey="secret") 
     break 
    except socket.error as ex: 
     if ex.errno != 98: 
      raise 
     port += 1 ## if errno==98, then port is not available. 

proc = subprocess.Popen((sys.executable, "subproc.py", str(port)), stdout=subprocess.PIPE, stderr=subprocess.PIPE) 

## open connection for remote process 
conn = l.accept() 
conn.send([1, "asd", None]) 
print(proc.stdout.readline()) 

subproc.py:

import multiprocessing.connection 
import subprocess 
import sys, os, time 

port = int(sys.argv[1]) 
conn = multiprocessing.connection.Client(('localhost', port), authkey="secret") 

while True: 
    try: 
     obj = conn.recv() 
     print("received: %s\n" % str(obj)) 
     sys.stdout.flush() 
    except EOFError: ## connection closed 
     break 

Bạn cũng có thể muốn xem câu trả lời đầu tiên this question để có được non-blocking đọc từ tiến trình con .

1

Tôi không nghĩ rằng bạn có tùy chọn tốt hơn là chuyển hướng một quy trình con sang tệp như bạn đã đề cập trong nhận xét của mình.

Cách điều khiển stdin/out/err hoạt động trong cửa sổ là mỗi quá trình khi được sinh ra có số std handles được xác định. Bạn có thể thay đổi chúng với SetStdHandle. Khi bạn sửa đổi của python sys.stdout bạn chỉ sửa đổi nơi python in ra công cụ, không phải nơi khác DLL đang in công cụ. Một phần của CRT trong DLL của bạn đang sử dụng GetStdHandle để tìm ra nơi in ra. Nếu bạn muốn, bạn có thể làm bất cứ đường ống nào bạn muốn trong cửa sổ API trong DLL của bạn hoặc trong kịch bản python của bạn với pywin32. Mặc dù tôi nghĩ nó sẽ đơn giản hơn với subprocess.

0

Tôi cho rằng tôi là cơ sở và thiếu cái gì đó, nhưng đối với những gì nó đáng giá ở đây là những gì tôi nghĩ đến khi tôi đọc câu hỏi của bạn.

Nếu bạn có thể chặn tất cả stdout và stderr (tôi nhận được hiển thị đó từ câu hỏi của bạn), thì tại sao không thêm hoặc quấn chức năng chụp xung quanh mỗi quy trình của bạn? Sau đó gửi những gì được chụp thông qua một hàng đợi cho một người tiêu dùng có thể làm bất cứ điều gì bạn muốn với tất cả các kết quả đầu ra?

Các vấn đề liên quan