2008-10-27 7 views
15

Mam metodę, którą chciałbym wywołać. Jednak szukam czystego, prostego sposobu na zabicie go lub zmusić go do powrotu, jeśli trwa to zbyt długo.Jak zawinąć metodę, aby zabić jej wykonanie, jeśli przekroczy określony czas oczekiwania?

Używam Java.

do zilustrowania:

logger.info("sequentially executing all batches..."); 
for (TestExecutor executor : builder.getExecutors()) { 
logger.info("executing batch..."); 
executor.execute(); 
} 

I postać klasa TestExecutor powinny implement Callable i dalej w tym kierunku.

Ale wszystko, co chcę zrobić, to zatrzymać executor.execute(), jeśli zabiera to zbyt dużo czasu.

Sugestie ...?

EDIT

Wiele sugestii otrzymanych założyć, że sposób jest wykonywany, że trwa długo zawiera jakąś pętlę i że zmienna może być okresowo sprawdzane. Jednak tak nie jest. Więc coś, co niekoniecznie będzie czyste, a które po prostu zatrzyma egzekucję, gdziekolwiek jest, jest do przyjęcia.

Odpowiedz

7

Zakładam użycie wielu wątków w następujących instrukcjach.

Zrobiłem trochę czytania w tej dziedzinie i większość autorów mówi, że to zły pomysł, aby zabić inny wątek.

Jeśli funkcja, którą chcesz zabić, można zaprojektować tak, aby okresowo sprawdzała prymityzm zmiennej lub synchronizacji, a następnie kończyła czysto, jeśli ta zmienna lub prymityw synchronizacji jest ustawiona, to byłoby całkiem czyste. Następnie jakiś wątek monitora może spać przez kilka milisekund, a następnie ustawić prymityzm zmiennej lub synchronizacji.

+0

dzięki za odpowiedź. w tym przypadku funkcja nie może być zaprojektowana do okresowego sprawdzania wartości podstawowej lub synchronizacji. – carrier

3

Naprawdę, nie możesz ... Jedynym sposobem, aby to zrobić, jest użycie thread.stop, uzgodnienie metody "współpracy" (np. Sprawdzanie okazyjnie dla Thread.isInterrupted lub wywoływanie metody powodującej wyjątek InterruptedException , np. Thread.sleep()), lub w jakiś sposób wywołać metodę w innej maszynie JVM całkowicie.

Dla niektórych rodzajów testów wywołanie metody stop() jest w porządku, ale prawdopodobnie spowoduje to uszkodzenie stanu zestawu testowego, więc po każdym wywołaniu należy ponownie uruchomić maszynę JVM(), jeśli chcesz uniknąć efekty interakcji.

Aby dobrze opisać, jak wdrożyć podejście oparte na współpracy, należy zapoznać się z Sun's FAQ on the deprecated Thread methods.

Dla przykładu tego podejścia w rzeczywistości, obiekt "IProgressMonitor" pozwala niektórym służbom zarządzającym sygnalizować podprocesy (za pomocą metody "anulowania"), że powinny się zatrzymać. Oczywiście opiera się to na metodach faktycznego sprawdzania metody isCancelled regularnie, czego często nie robią.

Podejście hybrydowe może polegać na ładnym zadawaniu pytania z przerwaniem, a następnie naleganiu kilka sekund później z zatrzymaniem. Ponownie, nie powinieneś używać stopu w kodzie produkcyjnym, ale może to być w porządku w tym przypadku, szczególnie. jeśli wkrótce opuścisz maszynę JVM.

Aby przetestować to podejście, napisałem prostą wiązkę, która uruchamia się i próbuje ją wykonać. Zapraszam do komentowania/edycji.

public void testStop(Runnable r) { 
    Thread t = new Thread(r); 
    t.start(); 
    try { 
     t.join(2000); 
    } catch (InterruptedException e) { 
     throw new RuntimeException(e); 
    } 

    if (!t.isAlive()) { 
     System.err.println("Finished on time."); 
     return; 
    } 

    try { 
     t.interrupt(); 
     t.join(2000); 
     if (!t.isAlive()) { 
      System.err.println("cooperative stop"); 
      return; 
     } 
    } catch (InterruptedException e) { 
     throw new RuntimeException(e); 
    } 
    System.err.println("non-cooperative stop"); 
    StackTraceElement[] trace = Thread.getAllStackTraces().get(t); 
    if (null != trace) { 
     Throwable temp = new Throwable(); 
     temp.setStackTrace(trace); 
     temp.printStackTrace(); 
    } 
    t.stop(); 
    System.err.println("stopped non-cooperative thread"); 
} 

By to sprawdzić, napisałem dwa konkurujące ze sobą nieskończone pętle, jedna spółdzielni, i jeden, który nie sprawdza wątku przerwaną trochę.

public void cooperative() { 
    try { 
     for (;;) { 
      Thread.sleep(500); 
     } 
    } catch (InterruptedException e) { 
     System.err.println("cooperative() interrupted"); 
    } finally { 
     System.err.println("cooperative() finally"); 
    } 
} 

public void noncooperative() { 
    try { 
     for (;;) { 
      Thread.yield(); 
     } 
    } finally { 
     System.err.println("noncooperative() finally"); 
    } 
} 

Wreszcie napisałem testy (JUnit 4) wykonywać je:

@Test 
public void testStopCooperative() { 
    testStop(new Runnable() { 
     @Override 
     public void run() { 
      cooperative(); 
     } 
    }); 
} 

@Test 
public void testStopNoncooperative() { 
    testStop(new Runnable() { 
     @Override 
     public void run() { 
      noncooperative(); 
     } 
    }); 
} 

nigdy nie używany Thread.stop() przed, więc byłem nieświadomy jego funkcjonowania. Działa poprzez rzucanie obiektu ThreadDeath z dowolnego miejsca, w którym aktualnie uruchomiony jest wątek docelowy. To rozszerza błąd. Tak więc, choć nie zawsze działa to czysto, zwykle pozostawia proste programy z dość rozsądnym stanem programu. Na przykład wywoływane są wszystkie bloki końcowe. Jeśli chcesz być prawdziwym palantem, możesz złapać ThreadDeath (lub Error), i nadal działać, tak!

Jeśli nic więcej, to naprawdę sprawia, że ​​chcą więcej kodu następuje podejście IProgressMonitor - Dodanie kolejnego parametru do metod, które może potrwać i zachęcającą implementor metody od czasu do czasu odpytywać monitor obiektu, aby sprawdzić, czy użytkownik chce, aby system się poddał. Spróbuję nadążyć za tym wzorem w przyszłości, szczególnie metody, które mogą być interaktywne. Oczywiście, nie zawsze wiesz z góry, które metody będą używane w ten sposób, ale do tego właśnie służą Profilery.

Jeśli chodzi o metodę "rozpocznij zupełnie inną maszynę JVM", zajmie to więcej pracy. Nie wiem, czy ktoś napisał delegujący moduł ładujący, lub jeśli jest uwzględniony w JVM, ale to byłoby wymagane w tym podejściu.

8

Java's interruption mechanism jest przeznaczony do tego rodzaju scenariusza. Jeśli metoda, którą chcesz przerwać, wykonuje pętlę, po prostu sprawdź ją w każdej iteracji wątku: interrupted status. Jeśli zostanie przerwany, należy rzucić wyjątek InterruptedException.

Następnie, gdy chcesz przerwać, po prostu musisz wywołać interrupt w odpowiednim wątku.

Alternatywnie możesz użyć opcji approach Sun jako alternatywy dla przestarzałej metody stop. Nie pociąga to za sobą odrzucania wyjątków, metoda normalnie powróciłaby normalnie.

+0

Niestety nie ma pętli. – carrier

1

Nikt nie odpowiedział bezpośrednio, więc tutaj jest najbliższa rzecz mogę ci dać w krótkim kodem psuedo:

zawinąć metodę w runnable/wymagalne. Sama metoda będzie musiała sprawdzić stan przerwania, jeśli chcesz go zatrzymać (na przykład, jeśli ta metoda jest pętlą, w pętli sprawdzana jest opcja Thread.currentThread(). Jeśli tak, zatrzymaj pętlę (wykład 't sprawdzaj każdą iterację, albo po prostu spowolnimy pracę w metodzie zawijania użyj thread.join (timeout), aby poczekać na czas, w którym chcesz uruchomić metodę, lub, w pętli, zadzwoń dołączaj wielokrotnie z mniejszym czasem oczekiwania, jeśli musisz robić inne rzeczy podczas oczekiwania, jeśli metoda się nie zakończy, po dołączeniu użyj powyższych zaleceń, aby przerwać szybkie/czyste.

więc kod mądry, stary kod:

void myMethod() 
{ 
    methodTakingAllTheTime(); 
} 

nowy kod:

void myMethod() 
{ 
    Thread t = new Thread(new Runnable() 
     { 
      public void run() 
      { 
       methodTakingAllTheTime(); // modify the internals of this method to check for interruption 
      } 
     }); 
    t.join(5000); // 5 seconds 
    t.interrupt(); 
} 

ale znowu, za to dobrze działać, będziesz jeszcze musiał zmodyfikować methodTakingAllTheTime albo że wątek będzie tylko kontynuuj działanie po wywołaniu przerwania.

+0

W rzeczywistości nie dojdziesz do t.interrupt(). Thread.join spowoduje przerwanie wyjątku, a t.interrupt zostanie ominięty. Właściwie od czasu sprawdzenia wyjątku InterruptedException powyższe nie zostanie skompilowane. Hmmm ... powinienem sprawdzić siebie, zanim się zniszczę ... – krakatoa

+0

Myliłem się co do użycia thread.join wyjątku interruptedException. To będzie po prostu kontynuowane. Twój kod jest poprawny, modulo brak deklaracji rzutów. – krakatoa

11

Należy spojrzeć na tych klasach: FutureTask, Callable, Executors

Oto przykład:

public class TimeoutExample { 
    public static Object myMethod() { 
     // does your thing and taking a long time to execute 
     return someResult; 
    } 

    public static void main(final String[] args) { 
     Callable<Object> callable = new Callable<Object>() { 
      public Object call() throws Exception { 
       return myMethod(); 
      } 
     }; 
     ExecutorService executorService = Executors.newCachedThreadPool(); 

     Future<Object> task = executorService.submit(callable); 
     try { 
      // ok, wait for 30 seconds max 
      Object result = task.get(30, TimeUnit.SECONDS); 
      System.out.println("Finished with result: " + result); 
     } catch (ExecutionException e) { 
      throw new RuntimeException(e); 
     } catch (TimeoutException e) { 
      System.out.println("timeout..."); 
     } catch (InterruptedException e) { 
      System.out.println("interrupted"); 
     } 
    } 
} 
+3

Jedyne, co chciałbym dodać to: 'finally {executorService .shutdown(); } ', aby zrobić porządek. – Greg

0

Poprawną odpowiedzią jest, wierzę, aby utworzyć Runnable wykonanie europejskiego podprogram i uruchom go w osobnym wątku. Runable może być FutureTask, który możesz uruchomić z timeoutem (metoda "get"). Jeśli to czasy się, dostaniesz TimeoutException, w którym proponujemy

  • rozmowę thread.interrupt(), aby próbować, aby zakończyć ją w pół-spółdzielczego sposób (wiele połączeń biblioteki wydają się być wrażliwe na ten , więc to prawdopodobnie będzie działać)
  • trochę poczekać (Thread.Sleep (300))
  • a następnie, jeśli wątek jest nadal aktywny (thread.isActive()), zadzwoń thread.stop(). Jest to nieaktualna metoda, ale najwyraźniej jedyną grą w mieście jest brak osobnego procesu ze wszystkim, co to powoduje.

W mojej aplikacji, gdzie biegnę niezaufane, uncooperative kod napisany przez moich początkujących studentów, robię wyżej, zapewniając, że nigdy nie zabił gwint (zapis) dostępu do wszelkich obiektów, które przetrwały jego śmierć. Obejmuje to obiekt zawierający wywoływaną metodę, która jest odrzucana, jeśli wystąpi przekroczenie limitu czasu. (Mówię moim studentom, aby uniknęli limitu czasu, ponieważ ich agent zostanie zdyskwalifikowany.) Nie jestem pewien co do wycieków pamięci ...

Rozróżniam długie runtime (metoda kończy się) i twarde limity czasu - twarde limity czasu są dłuższe i oznaczają złapać przypadek, gdy kod w ogóle się nie kończy, w przeciwieństwie do powolności.

Z moich badań wynika, że ​​Java nie ma niezasłużonego postanowienia do uruchamiania niewspółpracującego kodu, co w pewnym sensie stanowi dziurę w modelu bezpieczeństwa. Albo mogę uruchomić obcy kod i kontrolować uprawnienia, które ma (SecurityManager), albo nie mogę uruchomić obcego kodu, ponieważ może to skończyć zajmowaniem całego procesora bez żadnych nieużywanych środków, aby go zatrzymać.

double x = 2.0; 
while(true) {x = x*x}; // do not terminate 
System.out.print(x); // prevent optimization 
0

Mogę wymyślić nie tak świetny sposób na zrobienie tego. Jeśli potrafisz wykryć, kiedy zajmuje to zbyt dużo czasu, możesz sprawdzić metodę boolowskiej w każdym kroku. Niech program zmieni wartość boolean tooMuchTime na true, jeśli zabiera to zbyt dużo czasu (nie mogę temu zaradzić). Następnie użyj czegoś takiego:

Method(){ 
//task1 
if (tooMuchTime == true) return; 
//task2 
if (tooMuchTime == true) return; 
//task3 
if (tooMuchTime == true) return; 
//task4 
if (tooMuchTime == true) return; 
//task5 
if (tooMuchTime == true) return; 
//final task 
    } 
Powiązane problemy