我正在 python 中运行一个程序的一些代码,当我运行 c++ 时,我需要处理它的标准输入,例如 scanf 和 cin。我正在使用 subprocess.Popen 运行编译后的 C++ 程序。当我不提供任何标准输入时,我预计它会超时并引发异常,但我的程序立即结束并在控制台上打印 Killed 。 这是我的课程的一部分,我相信这已经足够了
def run(
self, args: List[str] = [], stdin: str = "", timeout: int = 30
) -> Tuple[int, str, str]:
process = subprocess.Popen(
[os.path.join(self.temp_file_folder, "build", self.target_file_name)]
+ args,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
)
try:
stdout, stderr = process.communicate(input=stdin, timeout=timeout)
except subprocess.TimeoutExpired as e:
process.kill()
raise RunTimeoutError(f"Expired in {timeout}s")
return (process.returncode, stdout, stderr)
当我使用30s时,程序在运行process.communicate后直接退出并在控制台上打印
Killed
没有任何异常。但是当我使用 5s 时,它会像我预期的那样抛出异常。
我在没有任何标准输入的情况下调用我的函数。
当 timeout == 30 时输出如下:
Killed
当 timeout == 5 时输出如下:
Traceback (most recent call last):
File "/home/jayzhu/projects/code-auto-comment/src/runner/__init__.py", line 161, in <module>
res = runner.run(timeout=5)
File "/home/jayzhu/projects/code-auto-comment/src/runner/__init__.py", line 124, in run
raise RunTimeoutError(f"Expired in {timeout}s")
__main__.RunTimeoutError: Expired in 5s
这是我当时运行的 C++ 代码
#include <iostream>
#include <cmath>
#include <limits>
#include <iomanip>
int main()
{
int N;
double sum = 0;
double product = 1;
std::cout << "Please enter an integer N between 1 and 50: ";
std::cin >> N;
while (std::cin.fail() || N < 1 || N > 50)
{
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\\n');
std::cout << "Input error, please enter an integer N between 1 and 50: ";
std::cin >> N;
}
std::cout << N << ":" << std::endl;
for (int i = N - 1; i >= 0; i--)
{
int value = pow(2, i);
sum += value;
product *= value;
std::cout << value << (i > 0 ? ", " : "\\n");
}
double geometric_mean = pow(product, 1.0 / N);
std::cout << std::fixed << std::setprecision(2);
std::cout << "sum: " << sum << std::endl;
std::cout << "avg: " << geometric_mean << std::endl;
return 0;
}
I不太确定这是 c++ 代码的问题,还是我自己的代码的问题
我的环境是
python 3.8.10
Ubuntu 20.04.6 LTS
问题出在你的 C++ 代码和
process.communicate()
的交互上。以下是发生的情况:
-
C++ 代码等待输入: 你的 C++ 代码使用
std::cin >> N;
来等待用户输入一个整数。 -
process.communicate(input=stdin, timeout=timeout)
被调用: 你使用process.communicate()
发送输入并等待进程完成。 -
死锁情况: 当你没有提供任何标准输入 (
stdin=""
) 时,就会出现问题。C++ 代码停滞,等待输入,而process.communicate()
则在等待 C++ 代码完成,从而导致死锁。 -
超时差异:
-
当
timeout=30
时,process.communicate()
会等待 30 秒,然后在进程仍在运行时被你的系统杀死,导致 "Killed" 消息。 -
当
timeout=5
时,process.communicate()
会在 5 秒后超时,并引发你捕获到的subprocess.TimeoutExpired
异常。
解决方案:
你有几种选择来解决这个问题:
1. 修改 C++ 代码:
-
添加超时: 你可以修改 C++ 代码,使其在没有接收到输入的情况下超时。这可以通过使用多线程或非阻塞 I/O 来实现。
-
默认值: 如果未提供输入,则使用默认值。
2. 在单独的线程中处理输入:
- 创建一个单独的线程来处理与 C++ 进程的输入。
-
使用
process.stdin.write()
发送输入。 -
使用
process.stdout.readline()
或类似方法从进程读取输出。
3. 使用
pexpect
库(如果适用):
-
如果你需要与交互式程序进行更复杂的交互,
pexpect
库可以提供帮助。它允许你发送输入、预期模式,并根据需要做出响应。
示例(使用默认值修改 C++ 代码):
#include <iostream>
#include <cmath>
#include <limits>
#include <iomanip>
int main() {
int N = 1; // 默认值
std::cout << "请输入一个介于 1 和 50 之间的整数 N(或按 Enter 使用默认值):";
std::string input;
std::getline(std::cin, input); // 读取整行输入
if (!input.empty()) {
// 如果用户提供了输入,则尝试将其转换为整数
std::stringstream ss(input);
ss >> N;
if (ss.fail() || N < 1 || N > 50) {
std::cerr << "输入错误,使用默认值 N = 1\n";
N = 1;
}
}
// ...(其余代码)
}
通过这种修改,即使没有提供输入,C++ 代码也会运行并使用默认值
N=1
。