我有一些C#代码,需要调用一个Python脚本数千次,每次传递一个字符串,并期望返回一个浮点数。这个Python脚本可以使用任何版本的Python运行,因此我不能使用Iron Python。有人建议我使用IPC命名管道。我没有经验,在C#和Python之间如何实现这个功能还无法解决。这是一个简单的过程,还是需要付出相当多的工作?这是否是解决我的问题的最佳方式?
我有一些C#代码,需要调用一个Python脚本数千次,每次传递一个字符串,并期望返回一个浮点数。这个Python脚本可以使用任何版本的Python运行,因此我不能使用Iron Python。有人建议我使用IPC命名管道。我没有经验,在C#和Python之间如何实现这个功能还无法解决。这是一个简单的过程,还是需要付出相当多的工作?这是否是解决我的问题的最佳方式?
使用zeromq。
以下是我使用zeromq的答案https://dev59.com/2mAg5IYBdhLWcg3w5ucd#23227631
它用于不同Python程序之间的消息传递,但是同样的通信方法也可以用于其他平台。只需使通过传递的消息具有互操作性-双方都必须正确理解内容。您可以使用二进制数据进行实验,但是很多时候,json表示法也非常快速和容易。但是还有许多序列化框架,如结果缓冲区等,可以帮助您对结果进行编码。
python -m venv [应创建环境的路径]
。sudo apt-get install python[python版本]-venv
,以下载具有创建Python虚拟环境功能的软件包,例如:sudo apt-get install python3.11-venv
。
环境初始化之后,进入 Windows 上的 python.exe
所在目录,或者 Linux 上的 python
二进制文件所在目录,并创建或上传您需要运行的 Python 脚本文件。
一种优雅而稳定的进程间通信方法是通过在脚本初始化时传递参数来调用脚本。如果您只需要向脚本传递一些信息,并且不需要在C#应用程序和Python应用程序之间进行数据交换,则此方法非常适合。
[ C# 代码 ]
static void Main(string[] args)
{
System.Diagnostics.Process proc = new System.Diagnostics.Process(); // <----- Process object
proc.StartInfo.WorkingDirectory = "C:\\Users\\Teodor Mihail\\source\\repos\\Inter_Process_Communication_Example_Python\\Inter_Process_Communication_Example_Python\\bin\\Debug\\Scripts"; // <----- Path where python executable is located
proc.StartInfo.FileName = "python.exe"; // <----- Executable name ( Windows ) or binary (Linux/MacOS)
proc.StartInfo.Arguments = "main.py Param1 Param2"; // // <----- Python file to be executed by the Python executable and the command line arguments passed to the process
proc.Start(); // <---- Start the process
Console.ReadLine();
}
[ Python 代码 ]
import sys
def main(param1, param2):
print("Param1 =", str(param1))
print("Param2=", str(param2))
# Get the first command line argument passed
# to the application and store it in a variable.
received_param1 = sys.argv[1]
# Get the second command line argument passed
# to the application and store it in a variable.
received_param2 = sys.argv[2]
# Call the "main" function and pass the two command
# line arguments to the method as parameters
main(received_param1, received_param2)
input()
标准输入、标准输出和标准错误是应用程序的主要I/O流,由操作系统内核用于接收输入、发送输出以及发送与函数相关的错误消息。这些主要的I/O流可以用于进程间通信,通过将这些I/O流的流向从子进程重定向到操作系统,再到父进程来实现。
[ C# 代码 ]
static void Main(string[] args)
{
System.Diagnostics.Process proc = new System.Diagnostics.Process(); // <----- Process object
proc.StartInfo.WorkingDirectory = "C:\\Users\\Teodor Mihail\\source\\repos\\Inter_Process_Communication_Example_Python\\Inter_Process_Communication_Example_Python\\bin\\Debug\\Scripts"; // <----- Path where python executable is located
proc.StartInfo.FileName = "python.exe"; // <----- Executable name ( Windows ) or binary (Linux/MacOS)
proc.StartInfo.Arguments = "main.py"; // <----- Python file to be executed by the Python executable
proc.StartInfo.RedirectStandardInput = true; // <----- Redirect the Stdin stream of the Python application through the C# application
proc.StartInfo.RedirectStandardOutput = true; // <----- Redirect the Stdout stream of the Python application through the C# application
proc.StartInfo.RedirectStandardError = true; // <----- Redirect the Stderr stream of the Python application through the C# application
proc.StartInfo.UseShellExecute = false; // <----- Do not use the OS shell to execute the application and use the C# application as the shell
proc.Start(); // <---- Start the process
// Read the output of the Python application on the Stdout stream
char[] buffer = new char[1000];
proc.StandardOutput.Read(buffer, 0, buffer.Length);
Console.WriteLine(buffer);
// Send a message to the Python application through the Stdin stream
proc.StandardInput.WriteLine("Hello from C# application over STDIN");
proc.StandardInput.FlushAsync();
// Read the output of the Python application on the Stdout stream
buffer = new char[1000];
proc.StandardOutput.Read(buffer, 0, buffer.Length);
Console.WriteLine(buffer);
// Read the error message thrown by the Python application on the Stderr stream
buffer = new char[1000];
proc.StandardError.Read(buffer, 0, buffer.Length);
Console.WriteLine(buffer);
Console.ReadLine();
}
[ Python 代碼 ]
import sys
def main():
# Send a message to the C# application on the Stdout stream
sys.stdout.write("Hello from Python application over STDOUT")
# Receive a message from the C# application on the
# Stdin stream and store it inside a variable.
received = input()
# Send the message received from the C# application
# back to the C# application through the Stdout stream
sys.stdout.write(received)
# Send an error message through the Stderr stream to the C# application
raise Exception("\n\n\nHello from Python application over STDERR")
main()
必须逐个字符地读取stdout和stderr中的数据,否则流可能会在等待响应状态下锁定。这是因为stdout和stderr不是异步I/O流,这可能会导致流在等待缓冲数据时被锁定:https://devblogs.microsoft.com/oldnewthing/20110707-00/?p=10223
char[] buffer = new char[1000];
proc.StandardOutput.Read(buffer, 0, buffer.Length);
管道是一种套接字类型,它使用操作系统文件系统在连接上发送和接收信息。这种IPC类型非常适合快速数据传输,并且相对于stdin、stdout和stderr具有多个运行连接的优势:https://www.baeldung.com/cs/pipes-vs-sockets。
[ C# 代码 ]
static void Main(string[] args)
{
System.Diagnostics.Process proc = new System.Diagnostics.Process(); // <----- Process object
proc.StartInfo.WorkingDirectory = "C:\\Users\\Teodor Mihail\\source\\repos\\Inter_Process_Communication_Example_Python\\Inter_Process_Communication_Example_Python\\bin\\Debug\\Scripts"; // <----- Path where python executable is located
proc.StartInfo.FileName = "python.exe"; // <----- Executable name ( Windows ) or binary (Linux/MacOS)
proc.StartInfo.Arguments = "main.py"; // <----- Python file to be executed by the Python executable
proc.Start(); // <---- Start the process
// Named pipe server object with an "Out" direction. This means that this pipe can only send messages
System.IO.Pipes.NamedPipeServerStream connection1 = new System.IO.Pipes.NamedPipeServerStream("main_write_pipe", System.IO.Pipes.PipeDirection.Out);
try
{
// Wait for a conection to e established on the pipe. This is a blocking method call, meaning that the thread will wait for this method to finish the execution
connection1.WaitForConnection();
// Byte buffer that stores the UTF8 encoded binary value of the string "Message from C# application over FIFO Pipe"
byte[] buffer = Encoding.UTF8.GetBytes("Message from C# application over FIFO Pipe");
// Write the binary buffer's contents on the pipe's I/O stream
connection1.Write(buffer, 0, buffer.Length);
// Flush the binary buffer's contents on the pipe's I/O stream
connection1.Flush();
}
catch
{
}
finally
{
if (connection1 != null)
{
connection1.Dispose();
}
}
// Named pipe server object with an "In" direction. This means that this pipe can only read messages
System.IO.Pipes.NamedPipeServerStream connection2 = new System.IO.Pipes.NamedPipeServerStream("main_read_pipe", System.IO.Pipes.PipeDirection.In);
try
{
// Wait for a conection to e established on the pipe. This is a blocking method call, meaning that the thread will wait for this method to finish the execution
connection2.WaitForConnection();
// Byte buffer that stores the UTF8 encoded binary value of the string "Message from Python application over FIFO Pipe"
byte[] buffer = new byte[1024];
connection2.Read(buffer, 0, buffer.Length);
// Print the message
Console.WriteLine(Encoding.UTF8.GetString(buffer));
}
catch
{
}
finally
{
if (connection1 != null)
{
connection1.Dispose();
}
}
Console.ReadLine();
}
[ Python 代码 ]
def main():
# On Linux and MacOs the pipes created by C# are located in "/tmp" so you have to enter "/tmp/pipe_name"
# Open the OS's file system pipe FIFO and specify that it has only "read" permissions.
# This must be done because the "main_write_pipe" pipe server in C# is created with
# write permissions and the receiver can only read from the stream.
pipe_fifo1 = open(r"\\.\pipe\main_write_pipe", "r")
# Read the content from the stream and store it in a variable.
# Because the stream is buffered, the data will be received
# after the pipe server instance in the C# application is
# closed.
received = pipe_fifo1.readline()
# Open the OS's file system pipe FIFO and specify that it has only "write" permissions.
# This must be done because the "main_read_pipe" pipe server in C# is created with
# read permissions and the receiver can only write to the stream.
pipe_fifo1 = open(r"\\.\pipe\main_read_pipe", "w")
# Write the content to the pipe stream
pipe_fifo1.write("Message from Python over FIFO Pipe")
# Flush the stream to ensure that all the data within the
# stream is flushed to the receiver.
pipe_fifo1.flush()
input()
main()
static void Main(string[] args)
{
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.StartInfo.WorkingDirectory = "C:\\Users\\Teodor Mihail\\source\\repos\\Inter_Process_Communication_Example_Python\\Inter_Process_Communication_Example_Python\\bin\\Debug\\Scripts";
proc.StartInfo.FileName = "python.exe";
proc.StartInfo.Arguments = "main.py";
proc.Start();
Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
server.Bind(new System.Net.IPEndPoint(System.Net.IPAddress.Loopback, 80));
server.Listen(1);
try
{
Socket client = server.Accept();
try
{
byte[] buffer = Encoding.UTF8.GetBytes("Message from Python application over TCP");
client.Receive(buffer, 0);
Console.WriteLine(Encoding.UTF8.GetString(buffer));
buffer = Encoding.UTF8.GetBytes("Message from C# application over TCP");
client.Send(buffer, 0);
}
catch
{
}
finally
{
if(client != null)
{
client.Dispose();
}
}
}
catch
{
}
finally
{
if(server != null)
{
server.Dispose();
}
}
Console.ReadLine();
}
[ Python 代码 ]
import socket
def main():
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(("127.0.0.1", 80))
client.send("Message from Python application over TCP".encode("utf-8"))
info = client.recv(len("Message from C# application over TCP".encode("utf-8")))
print(info.decode("utf-8"))
input()
main()
[使用命名管道进行实时数据传输]
对我很有用,但需要做一些调整:我必须在前面声明两个 new System.IO.Pipes.NamedPipeServerStream()
管道(否则,Python脚本找不到 main_read_pipe);我还必须将 connection1.Write()
改为 connection1.WriteAsync()
,将 connection2.Read()
改为 connection2.ReadAsync()
。 - gl3yn根据您所说的,您可以连接到Python进程并捕获标准输出文本。简单、快速和可靠!