【问题标题】:How to fix this script so that it won't peg the CPU?如何修复此脚本以使其不会与 CPU 挂钩?
【发布时间】:2012-08-16 15:22:41
【问题描述】:

在我家的 Kubuntu 机器上,我正在运行一个脚本来在每次按键时发出哔哔声,无论哪个窗口或应用程序有焦点,改编自 this insightful page

#!/usr/bin/env python 

from Xlib.display import Display
import os
import sys

ZERO=[]
for i in range(0,32):
        ZERO.append(0)
ignorelist=[ZERO]

def main():    
        if os.getuid()==0:
                os.system("modprobe pcspkr")
                print("Speaker enabled, start as normal user")
                sys.exit()

        print("If no beep is heard, then run as root to enable pcspkr")

        disp = Display()
        while 1:
                keymap=disp.query_keymap()
                if keymap not in ignorelist:
                        os.system("beep")

if __name__ == '__main__':
        main()

该脚本运行良好,但它使我的双核 Intel 机器的两个 CPU 各占 80% 左右,因此我对这台机器几乎无能为力。如何在不干扰其运行的情况下降低这个简单脚本的 CPU 需求?换句话说,无论哪个窗口或应用程序有焦点,它都应该在按键的那一刻发出哔哔声。

如果这在 Python 中是不可能的,我应该考虑哪些其他技术? C?我假设存在一些通知应用程序按键的内核组件:KDE 还如何处理全局快捷方式?我怎样才能让我的申请也收到这些通知?

目标是在按下每个键的那一刻发出哔声,因为我正在训练我的手指在机械键盘上打字而不会触底但不会丢失按键。我刚从 Cherry Browns 毕业到 Cherry Blues,缺乏战术反馈需要一些时间来适应。

请注意,无论哪个窗口具有焦点,任何解决方案都必须发出哔声。该程序旨在用作将在我使用的所有应用程序的后台运行的守护程序。

谢谢。

【问题讨论】:

  • 您是否检查过哪些程序正在与 CPU 挂钩?像这样的单线程程序是否可以自行负责似乎值得怀疑。
  • 是的,我已经检查并终止此脚本可以消除 CPU 负载。重新启动它会再次创建 CPU 负载。有趣的是,反复按一个键发出哔哔声实际上减少了负载。

标签: python performance keyboard cpu xlib


【解决方案1】:

使用事件:

import Xlib
from Xlib.display import Display

display = Display()

screen = display.screen()
w = screen.root.create_window(0, 0, 100, 100, 1,
              screen.root_depth,
              event_mask = Xlib.X.KeyPressMask)
w.map()

while True:
    event = display.next_event()
    if event.type != Xlib.X.KeyPress:
        continue
    print "OHAI"

详情来自http://python-xlib.sourceforge.net/doc/html/python-xlib_10.html#SEC9

【讨论】:

  • 谢谢。简单地将if keycode_requires_beep(event.detail) 替换为else: 并将# beep 替换为os.system("beep"),脚本运行并且不锁定CPU,但它不会在按键时发出哔哔声。因为我使用的是简单的else,所以我会期待很多哔声,但不是没有哔声!你知道为什么会这样吗?
  • 不知道,但是您可以找到一些问题的答案以便找出答案。 1:您是否进入了那个哔声部分,而当您到达那里时却没有听到哔声? 2:当你击键时你会得到一个事件吗? 3:那个事件的类型是什么?所有这些问题都是非常标准的调试,当您知道您期望发生的事情不是您所看到的事情时。这个想法是不断地问“实际发生了什么?” “我期望会发生什么?”直到你发现你的期望和坏代码的恶性现实不一致。
  • 谢谢。我看到print("1") 在event = Display.next_event() 行之前被打印,但print("2") 在该行之后没有。它似乎挂在那条线上。
  • 嗯,这真的很快。您的问题的原因就在文档中:“为了避免让客户端充斥着他们不感兴趣的事件,他们必须明确告诉服务器他们感兴趣的事件。”您在创建窗口时这样做,这意味着我们需要创建一个窗口。我已将示例代码替换为执行此操作的示例代码(并且我已经实际测试过)。这两个变化是抓取显示和创建窗口的前奏,以及对比较检查事件类型的更正。
  • 谢谢。不幸的是,此方法仅在新窗口获得焦点时才有效。显然这对我的日常使用没有帮助。除了screen 之外,我没有看到任何可能合适的东西。还有其他选择吗?否则这将是一个很好的解决方案。无论如何,我从这篇文章中学到了很多东西。谢谢。
【解决方案2】:

您的 while 循环正在消耗您所有的 CPU 周期,因为它执行得尽可能快。您可以添加一个小的 sleep() 延迟,但您可能会错过一些关键事件。或者,考虑使用pyhook 模块来监控按键。

【讨论】:

  • 谢谢。我实际上使用的是 Kubuntu Linux,而不是 Windows。不过,不错的发现。
【解决方案3】:

您可以使用 nice 启动您的脚本。 nice 命令会降低脚本的优先级,这样它只会在系统无事可做时运行。这样它仍然会占用 CPU 周期,但您可以正常使用您的系统执行其他任务。

有关详细信息,请参阅手册页。

编辑:

要减少 CPU 使用率,您可以使用 time.sleep(0.01) 添加一个小的延迟。这将减少 CPU 负载,但会略微增加按键和发出蜂鸣声之间的时间。

【讨论】:

  • 谢谢。使用nice -n 19 启动应用程序仍然会占用 CPU,但至少系统响应更快。这种方法的缺点是仍然保持 CPU 监视器挂钩,因此它对于确定不相关的系统负载变得无用。此外,在 CPU 处于活动状态时,温度会保持很高。所以这是一个很好的解决方法,但我真的需要解决根本问题的东西。
  • Lanaru 的第一个建议是添加一个小的睡眠延迟。如果您使用 time.sleep(0.05),您的按键和哔声之间最多会有 50 毫秒的延迟。这将大大减少 CPU 负载。
  • 谢谢,回顾一下即使是 0.01 的延迟也会显着降低 CPU 使用率而不影响操作。
  • 为什么投反对票?我很想知道我的答案有什么问题?
  • 我投了赞成票,我有时不明白为什么有些问题在没有解释的情况下被否决。事实上,我希望看到如此惩罚不予置评的投票。
【解决方案4】:

您的程序正在占用 CPU,因为它正在运行一个无限循环,这将使您的 CPU 忙于每毫秒左右检查一次键盘状态,即使没有按下任何键也是如此。由于您的计算机没有迹象表明它可以停止,它会尽可能频繁地继续检查,从而消耗资源。

正如 Iain 所建议的,这个问题最优雅的解决方案是使用Display.next_event(),它会告诉程序等到收到新事件(例如按键)。在这段等待时间内,您的程序不会消耗大量 CPU,并且您的负载应该会大幅下降。

【讨论】:

  • 谢谢。不幸的是,Display.next_event() 似乎需要它自己的窗口,并且只能用于该窗口内的按键。我需要一个全球解决方案。
【解决方案5】:

从this answer to a previously posted question 工作,揭示了a Python module called pyxhook 是a program called pykeylogger 的一部分。

使用此pyxhook 模块,您应该能够使用以下代码在每次按键时发出哔声:

import pyxhook
import time
import os
import sys

def do_beep(event):
    os.system('beep')

hm = pyxhook.HookManager()
hm.HookKeyboard()
hm.KeyDown = do_beep
hm.start()
while True:
    try:
        time.sleep(10)
    except: 
        break
hm.cancel()

我无法正确测试它,因为我没有beep 命令。但是,它会为每个按键报告 sh: beep: command not found。

【讨论】:

  • 感谢您找到 Linux 版本的 pyhook!这种方案在 CPU 上要合理得多,但它会将运行速度过快的哔命令排队,因此不适合实时打字指示。
  • 您可以尝试os.system('beep&') 在后台运行每个哔声,尽管我不确定它们将如何相互交互。此外,某些版本的 beep 命令还允许自定义哔声——例如,减少持续时间。
  • 这是个好主意。其实我也试过改哔声但是Kubuntu doesn't seem to acknowledge the change.
  • 您似乎正在更改 X11 铃声设置,然后使用beep 命令触发铃声。我认为beep 命令直接与 PC 扬声器接口,因此很可能完全忽略您的 X11 铃声设置。尝试使用the options of the beep command itself。 beep 的手册页有几个示例,例如 beep -f 400 -l 10。还要注意beep 有-s 和-c 选项,您可以使用它们来避免为每一个 按键启动一个新的beep 进程。这也可能有助于(稍微)延迟。
  • 谢谢南德普!我什至没有想到使用哔声标志。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-13
  • 1970-01-01
  • 2019-05-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多