2009-09-13 14 views
9

Przesłałem zadanie za pomocą executorów i potrzebuję go zatrzymać po pewnym czasie (np. 5 minut). Próbowałem robić tak:Java Executors: jak mogę zatrzymać przesłane zadania?

for (Future<?> fut : e.invokeAll(tasks, 300, TimeUnit.SECONDS)) { 
     try { 
      fut.get(); 
     } catch (CancellationException ex) { 
      fut.cancel(true); 
      tasks.clear(); 
     } catch(ExecutionException ex){ 
      ex.printStackTrace(); //FIXME: gestita con printstack  
     } 
    } 

Ale zawsze pojawia się błąd: Mam wspólny wektor, który musi być zmodyfikowany przez zadania, a następnie odczytać za pomocą gwintu, a nawet gdybym zatrzymać wszystkie zadania, Jeśli wystąpi przekroczenie limitu czasu, otrzymam:

Exception in thread "Thread-1" java.util.ConcurrentModificationException 

Czy coś jest nie tak? Jak mogę zatrzymać przesłane zadania, które nadal działają po 5 minutach?

+0

@Raffaele Di Fazio: Mam sformatowany kod - i dodaje bliskie klamrach, należy sprawdzić pod kątem dokładności. – akf

+0

Dziękuję, przepraszam za nieprawidłowe formatowanie. – Raffo

Odpowiedz

20

Tylko dlatego, że wywołanie cancel() na Future nie oznacza, że ​​zadanie zatrzyma się automatycznie. Musisz popracować w ramach zadania, aby upewnić się, że to się skończy:

  • Zastosowanie cancel(true) tak że przerwanie jest wysyłany do zadania.
  • Uchwyt InterruptedException. Jeśli funkcja w twoim zadaniu zgłasza InterruptedException, upewnij się, że jak najszybciej wyjdziesz z gracją, gdy tylko wyjdziesz z wyjątku.
  • Okresowe sprawdzanie Thread.currentThread().isInterrupted(), jeśli zadanie wykonuje ciągłe obliczenia.

Na przykład:

class LongTask implements Callable<Double> { 
    public Double call() { 

     // Sleep for a while; handle InterruptedException appropriately 
     try { 
      Thread.sleep(10000); 
     } catch (InterruptedException ex) { 
      System.out.println("Exiting gracefully!"); 
      return null; 
     } 


     // Compute for a while; check Thread.isInterrupted() periodically 
     double sum = 0.0; 
     for (long i = 0; i < 10000000; i++) { 
      sum += 10.0 
      if (Thread.currentThread().isInterrupted()) { 
       System.out.println("Exiting gracefully"); 
       return null; 
      } 
     } 

     return sum; 
    } 
} 

Również inne posty wspomniałem: ConcurrentModificationException mogą być wyrzucane nawet w przypadku korzystania z wątku bezpieczny Vector klasę, bo iteratorów można uzyskać od Vector nie jest bezpieczny wątku, i dlatego muszą być zsynchronizowane. Zaawansowana do pętli wykorzystuje iteratory, więc uważaj:

final Vector<Double> vector = new Vector<Double>(); 
vector.add(1.0); 
vector.add(2.0); 

// Not thread safe! If another thread modifies "vector" during the loop, then 
// a ConcurrentModificationException will be thrown. 
for (Double num : vector) { 
    System.out.println(num); 
} 

// You can try this as a quick fix, but it might not be what you want: 
synchronized (vector) { // "vector" must be final 
    for (Double num : vector) { 
     System.out.println(num); 
    } 
} 
+0

Doskonale - jakoś nigdy nie natrafiłem na Nici. interrupted() - Mogę użyć tego jutro! –

+7

Po pierwsze, wywołanie future.cancel (true) nie robi absolutnie nic. Umowa invokeAll stwierdza, że ​​anuluje zadania przed powrotem, a implementacja używa ostatniego bloku, aby to zapewnić. Po drugie, nigdy nie wywołuj Thread.interrupted(), spowoduje to usunięcie przerwanego stanu wątku. Większość implementacji będzie chciała użyć Thread.isInterrupted(). Należy sprawdzić flagę. Po trzecie, nie musi obsługiwać wyjątku InterruptedException, chyba że używa metod blokujących, takich jak przejęcie blokady, a następnie kompilator zapewnia, że ​​jest. FutureTask będzie wychwytywać wyjątki. –

+1

@Tim Bender: Masz rację: future.cancel (true) nic nie robi, przetestowany przeze mnie. Ale nie zrozumiałem, co myślisz, że powinienem zrobić. – Raffo

0

Najczęstszym przypadkiem dla ConcurrentModificationException jest modyfikowanie vector w tym samym czasie, co proces iteracji. Często odbywa się to w jednym wątku. Musisz przytrzymać blokadę na Vector dla całej iteracji (i uważać, aby nie zakleszczenie).

+0

Tak, wiem dlaczego wyrzucany jest wyjątek, ale nie powinien. Ta iteracja jest po części kodu, który napisałem, więc jeśli kod działa dobrze, nie powinienem dostać wyjątku ... – Raffo

1

Numer ConcurrentModificationException pochodzi z Twojego połączenia z tasks.clear(), podczas gdy twój Exceutors iteruje po twoim tasks . Możesz spróbować zadzwonić pod numer shutdownNow() do swojego ExecutorService

+0

To nie wydaje się działać ... – Raffo

0

fut.get() jest wezwaniem blokowanie, nawet po timeout, będzie blokować aż zadanie zostanie wykonane. Jeśli chcesz zatrzymać się tak blisko znaku 5 minut, jak to możliwe, musisz sprawdzić flagę przerwania, po prostu polecam to zrobić za pomocą metody Thread.isInterrupted(), która zachowuje stan przerwania. Jeśli chcesz po prostu zatrzymać się natychmiast i nie trzeba czyścić żadnego stanu, a następnie rzucić wyjątek, który zostanie przechwycony przez Przyszłość i wskazany jako wyjątek ExecutionException.

fut.cancel (true) nie robi niczego, ponieważ metoda invokeAll() już to zrobiła.

Jeśli nie używasz kolekcji "zadania" w innym miejscu, prawdopodobnie nie musisz wywoływać polecenia clear(). To nie będzie źródłem twojego problemu, ponieważ metoda invokeAll() jest wykonywana z listą do czasu wywołania clear(). Jeśli jednak chcesz rozpocząć tworzenie listy nowych zadań do wykonania, proponuję utworzyć nową listę zadań, a nie używać starej listy nowych zadań.

Niestety, nie mam odpowiedzi na Twój problem. Nie widzę tu wystarczającej ilości informacji, aby ją zdiagnozować. Nic w podanym fragmencie kodu wskazuje na niewłaściwe (tylko niepotrzebne) użycie klas/metod biblioteki. Być może, jeśli podałeś pełny ślad stosu, zamiast błędu jednej linii.

+0

Użyłem kolekcji gdzieś indziej i jest ona w pętli while, więc musi być wyczyszczona, aby była pusta, gdy pętla się powtarza. Oczywiście mogę zrobić clear() po kodzie widocznym w poście i powinno to być w porządku. Ważna część mojego pytania nie jest wyjątkiem: muszę wiedzieć, jak zatrzymać przyszłość po 5 minutach i, oczywiście, spróbuję rzucić wyjątek zgodnie z sugestią. Mogę nawet zmienić sposób, w jaki przesyłam swoje zadania. Nauczyłem się w ten sposób tutaj: http://stackoverflow.com/questions/1322147/help-with-java-executors-wait-for-task-termination – Raffo

-1

Umieść fut.cancel(true); w końcu zablokować

+0

Masz na myśli dodanie bloku 'finally', prawda? –

Powiązane problemy