2009-11-05 18 views

Odpowiedz

21

To pytanie ma lepszą odpowiedź tutaj: Executing a function with a timeout

Futures na ratunek!

user=> (let [f (future (reduce * (range 1 1001)))] 
    (.get f 1 java.util.concurrent.TimeUnit/MILLISECONDS)) 
java.util.concurrent.TimeoutException (NO_SOURCE_FILE:0) 

I zrobić makro z nim:

(defmacro time-limited [ms & body] 
    `(let [f# (future [email protected])] 
    (.get f# ~ms java.util.concurrent.TimeUnit/MILLISECONDS))) 

Więc można to zrobić:

user=> (time-limited 1 (reduce * (range 1 1001))) 
java.util.concurrent.TimeoutException (NO_SOURCE_FILE:0) 
user=> (time-limited 1 (reduce * (range 1 101))) 
93326215443944152681699238856266700490715968264381621468592963895217599993229915 
608941463976156518286253697920827223758251185210916864000000000000000000000000 
+1

Po prostu być clear - przyszłość zdecydowanie działa, ale uruchomi dostarczony expr w osobnym wątku. Większość czasu to nie stanowi problemu, szczególnie jeśli piszesz referencyjny, przejrzysty kod, ale jeśli używasz (wiążąc ...) to może zdecydowanie powodować pewne problemy. – levand

+5

Przyszłość może nadal istnieć po upływie limitu czasu. Użyj 'future-cancel' po upływie limitu czasu, jak opisano [w tym wątku] (http://stackoverflow.com/a/6697469/682672). – neatonk

+0

Bardziej odporną wersję tego, która używa wątków zamiast futures można znaleźć [tutaj] (http://stackoverflow.com/a/6697356/682672) – neatonk

2

Nie jestem pewien, czy jest to możliwe bez uruchamiania wyrażenia w osobnym wątku. Powodem jest to, że jeśli wątek jest zajęty przetwarzaniem wyrażenia, nie można wstrzyknąć kodu, aby rzucić wyjątek.

Wersja z wątkiem monitora, który zgłasza wyjątek, jeśli wyrażenie trwa zbyt długo, jest zdecydowanie możliwa, jednak zgłoszony wyjątek pochodzi z wątku monitora, a nie z wątku, w którym wyrażenie jest uruchomione. Wtedy nie byłoby sposobu, aby powstrzymać go przed wysłaniem tego wątku przerwania, które mogłoby zignorować, gdybyś nie zakodował tego w wyrażeniu.

Jeśli możliwe jest posiadanie wersji, która uruchamia wyrażenie w oddzielnym wątku, daj mi znać i mogę wysłać przykładowy kod. W przeciwnym razie twój najlepszy zakład brzmi, jakby to było napisać główną pętlę/rekursję wyrażenia w taki sposób, aby sprawdzał, ile czasu zajęło mu wykonanie każdej iteracji i zgłasza wyjątek, jeśli przekroczył granicę. Przepraszam, jeśli to nie całkiem to, czego potrzebujesz ...

+0

Może (z-limitem czasu ... może działać jak pętla, i być punktem powtarzania, i za każdym razem, gdy trafisz (z-timeout sprawdza zegar i zatrzymuje wykonywanie (lub zgłasza wyjątek), jeśli zostało uruchomione do Jednak to użycie musiałoby być * wewnątrz * funkcji, nie owijać tej funkcji, ale nie powoduje efektów ubocznych w innych ogólnie zniechęconych funkcjach [Myślę o wszystkich ostrzeżeniach wokół (wiązanie)? – nilamo

1

natknąłem tego wątku niedawno podczas zadaje to samo pytanie. Nie byłem w pełni zadowolony z udzielonych odpowiedzi, więc wybrałem alternatywne rozwiązanie. To rozwiązanie uruchomi twój kod w bieżącym wątku i zakręcie przyszłości, aby go przerwać po ustawionym czasie oczekiwania w ms.

(defn invoke-timeout [f timeout-ms] 
    (let [thr (Thread/currentThread) 
     fut (future (Thread/sleep timeout-ms) 
        (.interrupt thr))] 
    (try (f) 
     (catch InterruptedException e 
      (throw (TimeoutException. "Execution timed out!"))) 
     (finally 
      (future-cancel fut))))) 

(defmacro timeout [ms & body] `(invoke-timeout (fn [] [email protected]) ~ms)) 

będzie go używać w kodzie tak:

(timeout 1000 your-code) 

LUB

(invoke-timeout #(your-code) 1000) 

Jedno zastrzeżenie, aby pamiętać, że your-code nie może złapać InterruptedException używany do wyzwalania TimeoutException. Używam tego do testowania i działa dobrze.

Aby uzyskać dodatkowe zastrzeżenia, zobacz stronę Thread.interrupt()javadoc.

Możesz zobaczyć ten kod w użyciu here.