检测进程是否为 运行 的最快方法
Fastest way to detect if a process is running
我需要我的对象“Launcher”来检测它的关联进程是否 运行ning。
我最初的解决方案是使用 psutil
简单地 运行 一个 for 循环
def Launcher(self, processName):
self.processName=processName
def process_up(self, attempts=0):
if attempts <= 3:
try:
if self.processName in (p.name() for p in psutil.process_iter()):
return True
else:
return False
except:
self.process_up(attempts=1)
else:
logging.error("Psutil Fatal Error. Unable to check status of process {}".format(self.processName))
return False
递归用于在 for 循环中检测到进程 p 但在调用 .name() 之前死亡的极少数情况。
无论如何,在我用我的所有进程(大约 40 个进程,所以 40 个启动器 运行ning)测试它之前,这一切听起来都很好而且花花公子,问题是 运行ning 这个循环大约需要 0.1 秒,总计约 4 秒。
但是,我需要瞄准 <1 秒。还有哪些超快速的方法可以确定给定进程是否 运行ning?我不需要知道关于进程的任何信息(我不关心它的 pid 或名称),只关心它是否启动。
附带说明:我不能使用多线程或任何类型的并行性。我必须按顺序 运行 这些启动器。
编辑:我也尝试了以下代码,这在性能方面绝对更好:
def process_up(self):
try:
call = subprocess.check_output("pgrep -f '{}'".format(self.processName), shell=True)
return True
except subprocess.CalledProcessError:
return False
现在代码 运行s 在 ~2 秒内完成,但仍然太多了
使用 pidof
应该比 pgrep
更快
def process_up(self):
try:
call = subprocess.check_output("pidof '{}'".format(self.processName), shell=True)
return True
except subprocess.CalledProcessError:
return False
根据 psutil doc,如果使用默认参数调用 process_iter() 会很慢。
这里您需要的是提供您需要的attributes列表。
例如你可以替换:
psutil.process_iter())
作者:
psutil.process_iter(['name','pid']))
此外,您可以像这样存储 pid:
if my_pid in psutil.pids():
# process still alive
如果您关心速度,为什么不使用 os
模块? os
模块提供最低级别的进程例程,并提供比调用 shell 可执行文件最佳的性能。如果您想要更高级别的东西,请考虑 subprocess
模块。
import os
import sys
from time import sleep
def child_process():
"""
Run your child process here
"""
print("child process started..")
sys.stdout.flush()
# put your external process here
sleep(10)
os.execv("/bin/echo", ["I am a child process"])
# should never get here!
print("I am in a bad place! - child")
os._exit(os.EX_SOFTWARE)
def process_up(pid):
"""
Is the process up?
:return: True if process is up
"""
try:
return None != os.waitpid(pid, os.WNOHANG)
except ChildProcessError: # no child processes
return False
def main():
pid = os.fork()
if pid == 0:
child_process()
else:
# main thread
# ...
checks=0
while process_up(pid):
checks += 1
print(f"Main: Process {pid} completed. {checks} checks in 10 seconds")
# Example output:
# Process 370 completed. 13307171 checks in 10 seconds.
if __name__ == "__main__":
main()
一个选择是集中检查,这样您就可以针对所有内容检查 运行 个进程列表一次,而不是每个受监控的进程一次。
monitored_processes = set(...) # list of names of processes
running_processes = set(p.name() for p in psutil.process_iter())
missing_processes = monitored_processes - running_processes
您需要一些额外的代码来重试,但这也不难:
missing_counts = Counter()
monitored_processes = set(...) # list of names of processes
while True:
running_processes = {p.name() for p in psutil.process_iter()}
missing_processes = monitored_processes - running_processes
for recovered_process in missing_counts.keys() - missing_processes:
del missing_counts[recovered_process]
for missing_process in missing_processes:
missing_counts[missing_process] += 1
down_processes = {
down_process
for down_process, count in missing_counts.items()
if count > 3
}
我不确定这是否是 Windows 的特定问题。
psutil
在我的机器上比 subprocess
+ tasklist
.
慢 2 倍以上
通常我的系统上有大约 400 个进程 运行ning。如果我想检查 Elite Dangerous 的启动器是否 运行ning,我有以下代码:
t0 = time.perf_counter()
st = 'EDLaunch.exe' in (p.name() for p in psutil.process_iter())
print(time.perf_counter() - t0)
print(st)
t0 = time.perf_counter()
s = subprocess.check_output('tasklist', shell=True).decode()
st = 'EDLaunch.exe' in s
print(time.perf_counter() - t0)
print(st)
当启动器不是运行ning时,输出是
0.8039536
False
0.3197087999999999
False
子进程要快得多。此外,使用循环而不是列表理解对于 psutil 来说不会更快。
其实还有更好的办法from here。
由于我们的目标是检查进程是否 运行ning,因此返回列表并进行检查没有意义。
所以我可以做到
t0 = time.perf_counter()
s = subprocess.check_output('tasklist /NH /FI "IMAGENAME eq EDLaunch.exe"', shell=True).decode()
st = 'EDLaunch.exe' in s
print(time.perf_counter() - t0)
print(st)
需要 0.1 秒才能完成。再次比简单的子流程方式快 3 倍。
唯一的缺点是,当进程未 运行ning 时,返回的消息类似于 INFO: No tasks are running which match the specified criteria.
,它与系统区域设置一致....您可能会以其他语言获得此输出如果您的 Windows 语言不是英语。
幸运的是,您可以 运行 命令 chcp 437
将输出更改为英语,而不必每次都 运行 运行 check_output
。只需在程序开始时执行一次即可。
所以我现在的方式是这样的
# make sure the output is in English
# this line only need to run once (os.system is also slower than subprocess somehow)
subprocess.run('chcp 437', shell=True)
s = subprocess.check_output('tasklist /NH /FI "IMAGENAME eq EDLaunch.exe"', shell=True).decode()
我需要我的对象“Launcher”来检测它的关联进程是否 运行ning。 我最初的解决方案是使用 psutil
简单地 运行 一个 for 循环def Launcher(self, processName):
self.processName=processName
def process_up(self, attempts=0):
if attempts <= 3:
try:
if self.processName in (p.name() for p in psutil.process_iter()):
return True
else:
return False
except:
self.process_up(attempts=1)
else:
logging.error("Psutil Fatal Error. Unable to check status of process {}".format(self.processName))
return False
递归用于在 for 循环中检测到进程 p 但在调用 .name() 之前死亡的极少数情况。
无论如何,在我用我的所有进程(大约 40 个进程,所以 40 个启动器 运行ning)测试它之前,这一切听起来都很好而且花花公子,问题是 运行ning 这个循环大约需要 0.1 秒,总计约 4 秒。
但是,我需要瞄准 <1 秒。还有哪些超快速的方法可以确定给定进程是否 运行ning?我不需要知道关于进程的任何信息(我不关心它的 pid 或名称),只关心它是否启动。
附带说明:我不能使用多线程或任何类型的并行性。我必须按顺序 运行 这些启动器。
编辑:我也尝试了以下代码,这在性能方面绝对更好:
def process_up(self):
try:
call = subprocess.check_output("pgrep -f '{}'".format(self.processName), shell=True)
return True
except subprocess.CalledProcessError:
return False
现在代码 运行s 在 ~2 秒内完成,但仍然太多了
使用 pidof
应该比 pgrep
def process_up(self):
try:
call = subprocess.check_output("pidof '{}'".format(self.processName), shell=True)
return True
except subprocess.CalledProcessError:
return False
根据 psutil doc,如果使用默认参数调用 process_iter() 会很慢。
这里您需要的是提供您需要的attributes列表。
例如你可以替换:
psutil.process_iter())
作者:
psutil.process_iter(['name','pid']))
此外,您可以像这样存储 pid:
if my_pid in psutil.pids():
# process still alive
如果您关心速度,为什么不使用 os
模块? os
模块提供最低级别的进程例程,并提供比调用 shell 可执行文件最佳的性能。如果您想要更高级别的东西,请考虑 subprocess
模块。
import os
import sys
from time import sleep
def child_process():
"""
Run your child process here
"""
print("child process started..")
sys.stdout.flush()
# put your external process here
sleep(10)
os.execv("/bin/echo", ["I am a child process"])
# should never get here!
print("I am in a bad place! - child")
os._exit(os.EX_SOFTWARE)
def process_up(pid):
"""
Is the process up?
:return: True if process is up
"""
try:
return None != os.waitpid(pid, os.WNOHANG)
except ChildProcessError: # no child processes
return False
def main():
pid = os.fork()
if pid == 0:
child_process()
else:
# main thread
# ...
checks=0
while process_up(pid):
checks += 1
print(f"Main: Process {pid} completed. {checks} checks in 10 seconds")
# Example output:
# Process 370 completed. 13307171 checks in 10 seconds.
if __name__ == "__main__":
main()
一个选择是集中检查,这样您就可以针对所有内容检查 运行 个进程列表一次,而不是每个受监控的进程一次。
monitored_processes = set(...) # list of names of processes
running_processes = set(p.name() for p in psutil.process_iter())
missing_processes = monitored_processes - running_processes
您需要一些额外的代码来重试,但这也不难:
missing_counts = Counter()
monitored_processes = set(...) # list of names of processes
while True:
running_processes = {p.name() for p in psutil.process_iter()}
missing_processes = monitored_processes - running_processes
for recovered_process in missing_counts.keys() - missing_processes:
del missing_counts[recovered_process]
for missing_process in missing_processes:
missing_counts[missing_process] += 1
down_processes = {
down_process
for down_process, count in missing_counts.items()
if count > 3
}
我不确定这是否是 Windows 的特定问题。
psutil
在我的机器上比 subprocess
+ tasklist
.
通常我的系统上有大约 400 个进程 运行ning。如果我想检查 Elite Dangerous 的启动器是否 运行ning,我有以下代码:
t0 = time.perf_counter()
st = 'EDLaunch.exe' in (p.name() for p in psutil.process_iter())
print(time.perf_counter() - t0)
print(st)
t0 = time.perf_counter()
s = subprocess.check_output('tasklist', shell=True).decode()
st = 'EDLaunch.exe' in s
print(time.perf_counter() - t0)
print(st)
当启动器不是运行ning时,输出是
0.8039536
False
0.3197087999999999
False
子进程要快得多。此外,使用循环而不是列表理解对于 psutil 来说不会更快。
其实还有更好的办法from here。 由于我们的目标是检查进程是否 运行ning,因此返回列表并进行检查没有意义。 所以我可以做到
t0 = time.perf_counter()
s = subprocess.check_output('tasklist /NH /FI "IMAGENAME eq EDLaunch.exe"', shell=True).decode()
st = 'EDLaunch.exe' in s
print(time.perf_counter() - t0)
print(st)
需要 0.1 秒才能完成。再次比简单的子流程方式快 3 倍。
唯一的缺点是,当进程未 运行ning 时,返回的消息类似于 INFO: No tasks are running which match the specified criteria.
,它与系统区域设置一致....您可能会以其他语言获得此输出如果您的 Windows 语言不是英语。
幸运的是,您可以 运行 命令 chcp 437
将输出更改为英语,而不必每次都 运行 运行 check_output
。只需在程序开始时执行一次即可。
所以我现在的方式是这样的
# make sure the output is in English
# this line only need to run once (os.system is also slower than subprocess somehow)
subprocess.run('chcp 437', shell=True)
s = subprocess.check_output('tasklist /NH /FI "IMAGENAME eq EDLaunch.exe"', shell=True).decode()