Dlaczego ten skrypt Pythona działa w tle i zużywa 100% procesora?

22

Chcę uruchomić prosty skrypt Pythona w tle, który odczytuje tekst ze schowka i drukuje go. Oto mój kod.

#!/usr/bin/env python

import Tkinter

last_clipboard = ""

def get_clipboard():
  global last_clipboard
  root = Tkinter.Tk()
  root.withdraw() # Hide the main window (optional)
  text_in_clipboard = root.clipboard_get()
  if text_in_clipboard != last_clipboard:
    last_clipboard = text_in_clipboard
    print last_clipboard


while True:
  get_clipboard()

Działa zgodnie z oczekiwaniami, ale zużywa zbyt dużo procesora (100% procesora).

Jak mogę sprawić, by działał poprawnie, nie zużywając tyle?

dmx
źródło
26
Jeśli w ogóle jest obsługiwany przez używaną strukturę, użyj kodu opartego na zdarzeniach, aby wykryć zmiany w schowku zamiast pętli. Istnieje różnica między ciągłym przechowywaniem schowka do momentu jego zmiany lub słuchaniem systemu informującego o zmianie.
Stefan
6
@dessert Nigdy nie robiłem tego w Pythonie, ale wydaje się, że jest to rozwiązanie z GTK: stackoverflow.com/a/25961646/985296 (nie wspomina o żadnej zależności platformy).
Stefan
@ jpmc26 & deser Wygląda jak meta dyskusja, zachęcamy do wzięcia go tam. Zdecydowanie dobrym pomysłem jest wyjaśnienie tego zakresu .
Maszt
1
@dessert Otwórz meta wątek, jeśli Ty i JPMC chcesz przedyskutować, czy jest to temat on / off. Nie używaj komentarzy do tego argumentu. (Czyszczenie komentarza zostało zakończone, temat zablokowany na tydzień w oczekiwaniu na dyskusję na Meta, ale także w celu zatrzymania argumentu komentarza)
Thomas Ward

Odpowiedzi:

44

Zapomniałeś time.sleep()w swojej whilepętli, zgodnie z tą odpowiedzią, że uśpienie SO przez 0.2s jest dobrym kompromisem między częstotliwością odpytywania a obciążeniem procesora:

import time

while True:
  get_clipboard()
  time.sleep(0.2) # sleep for 0.2 seconds

Sprawdzanie schowka co 0,2 sekundy wydaje się dość często; jeśli chcesz zmniejszyć obciążenie procesora, możesz nawet zwiększyć tę wartość - niewielu użytkowników zmienia zawartość schowka z jednej sekundy na drugą.

Zauważ, że ogólnie odpytywanie w pętli tak często, jak to nie jest uważane za dobry projekt. Lepszym rozwiązaniem byłoby działanie w przypadku zmiany zawartości schowka, przykład dla GTK można znaleźć w tej odpowiedzi SO .

Dalsza lektura

deser
źródło
3
Możesz skrócić interwał uśpienia bez faktycznego wpływu na używany procesor. Znalazłem na moim Macu: 0,01 s: 69%, 0,02 s: 43%, 0,05 s: 25%, 0,1 s: 14%, 0,2 s: 7%. 0,5 s: 3%
Floris
6
Sondowanie wciąż jest do bani, ponieważ budzi się ten proces, zanieczyszczając pamięć podręczną procesora i tak dalej. Jak omówiono w komentarzach, znacznie lepiej poczekać na powiadomienie o zmianie schowka.
Peter Cordes
@dessert: Gdybym znał odpowiedź, zrobiłbym to. Sugeruję jedynie wspomnieć w twojej odpowiedzi, że budzenie się co 0,2 sekundy wciąż nie jest uważane za dobry projekt, a szukanie podejścia bez odpytywania byłoby znacznie lepsze. Ale w przypadku jednorazowego hacka, który będzie działał tylko na jednym komputerze, z pewnością nie jest to okropne i prawdopodobnie jest wystarczająco dobre.
Peter Cordes
26

W końcu sprawiam, że działa bez pętli. To jest kod:

Musiałem zainstalować kilka modułów: sudo apt install python3-gi python3-gi-cairo gir1.2-gtk-3.0

#!/usr/bin/env python3
import gi, sys
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk

last_clipboard = ""

def callBack(*args):
  global last_clipboard
  new_clipboard = clip.wait_for_text()
  if new_clipboard != last_clipboard:
    last_clipboard = new_clipboard
    print("new Clipboard")
    print(new_clipboard)

clip = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
clip.connect('owner-change',callBack)
Gtk.main()

wybierz rozwiązanie, które najbardziej Ci odpowiada.

dmx
źródło
Oooh… Dlaczego prosisz clip.wait_for_text()dwa razy?
wizzwizz4
@ wizzwizz4 masz rację, edytowałem ... czołgi
dmx
@ wizzwizz4 Nie każdy egzemplarz dwukrotnie po prostu mieć pewność?
Michael Frank
16

Działasz w while True:pętli! Oznacza to, że procesor stale pracuje w pętli. Po prostu dodaj małą pauzę i powinieneś zobaczyć, jak gwałtownie spada użycie procesora:

#!/usr/bin/env python

import Tkinter
import time

last_clipboard = ""

def get_clipboard():
  global last_clipboard
  root = Tkinter.Tk()
  root.withdraw() # Hide the main window (optional)
  text_in_clipboard = root.clipboard_get()
  if text_in_clipboard != last_clipboard:
    last_clipboard = text_in_clipboard
    print last_clipboard

while True:
  get_clipboard()
  time.sleep(1)
terdon
źródło
3

Byłem zaintrygowany tym projektem, dlatego napisałem skrypt bash dla tych bardziej komfortowych w tym środowisku:

#!/bin/bash

xclip -o -sel clip > /tmp/LastClip

while true ; do 

    xclip -o -sel clip > /tmp/NewClip
    diff -q /tmp/LastClip /tmp/NewClip > /tmp/DiffClip
    if [[ -s /tmp/DiffClip ]] ; then
        cat /tmp/NewClip    # For testing dump to screen instead of printing
        cp -a /tmp/NewClip /tmp/LastClip
    fi
    sleep 1.0

done

Wymaga xclippakietu Xorg :

sudo apt install xclip

Zrzuca zawartość schowka do ekranu za pomocą catpolecenia. Jeśli chcesz wymienić dysk kopii zamiast catz lpi podać nazwę drukarki, orientacji i ewentualnie opcję „Dopasuj do strony”.

Zobaczysz trochę opóźnień w stosunku do ekranu, ponieważ wybieram, sleep 1.0który byłby niezauważalny w drukarce i wciąż szybciej niż ludzie mogą wyróżnić tekst i użyć Ctrl+ C.

Jeśli skopiujesz dokładnie ten sam podświetlony tekst do schowka, nie spowoduje to różnicy. Mniej więcej jedna litera wywoła odpowiedź.

WinEunuuchs2Unix
źródło