Chcę uruchomić wątek przez określony czas. Jeśli nie zostanie ukończony w tym czasie, chcę go zabić, zgłosić wyjątek lub jakoś sobie z nim poradzić. Jak można to zrobić?
Jednym ze sposobów zrobienia tego, jak się zorientowałem tym wątku, jest użycie TimerTask wewnątrz metody run () wątku.
Czy są na to jakieś lepsze rozwiązania?
EDYCJA: Dodanie nagrody, ponieważ potrzebowałem jaśniejszej odpowiedzi. Podany poniżej kod ExecutorService nie rozwiązuje mojego problemu. Dlaczego powinienem spać () po uruchomieniu (trochę kodu - nie mam żadnej kontroli nad tym fragmentem kodu)? Jeśli kod zostanie zakończony, a funkcja sleep () zostanie przerwana, jak może to być timeOut?
Zadanie, które należy wykonać, nie jest pod moją kontrolą. Może to być dowolny fragment kodu. Problem polega na tym, że ten fragment kodu może działać w nieskończonej pętli. Nie chcę, żeby tak się stało. Chcę tylko uruchomić to zadanie w osobnym wątku. Wątek nadrzędny musi poczekać, aż ten wątek się zakończy i musi znać status zadania (tj. Czy upłynął limit czasu lub wystąpił jakiś wyjątek, czy też powodzenie). Jeśli zadanie przejdzie w nieskończoną pętlę, mój wątek nadrzędny będzie czekał w nieskończoność, co nie jest idealną sytuacją.
źródło
sleep()
był tylko skrót do reprezentowania „długiego zadania”. Po prostu zastąp go swoim prawdziwym zadaniem;)interrupt()
wywołania w jego wątku ... nie wszystkie wywołania „blokujące” robią, jak starałem się wskazać w mojej odpowiedzi. Specyfika zadania, które próbujesz przerwać, ma ogromną różnicę w podejściu, które należy zastosować. Przydałoby się więcej informacji o zadaniu.Odpowiedzi:
Rzeczywiście raczej użyj
ExecutorService
zamiastTimer
, oto SSCCE :Zagraj trochę
timeout
argumentem wFuture#get()
metodzie, np. Zwiększ go do 5, a zobaczysz, że wątek się kończy. Możesz przechwycić limit czasu wcatch (TimeoutException e)
bloku.Aktualizacja: aby wyjaśnić nieporozumienie pojęciowe, nie
sleep()
jest wymagane. Jest po prostu używany do celów SSCCE / demonstracyjnych. Po prostu wykonaj swoje długofalowe zadanie w miejscu . W ramach długiego zadania powinieneś sprawdzić, czy wątek nie został przerwany w następujący sposób:sleep()
źródło
Thread.sleep(4000)
inną długo działającą instrukcję, a przykład nie zadziała. Innymi słowy, ten przykład działałby tylko wtedy, gdyTask
został zaprojektowany w celu zrozumieniaThread.isInterrupted()
zmiany statusu.Nie ma w 100% niezawodnego sposobu na zrobienie tego dla każdego starego zadania. Zadanie należy napisać z myślą o tej zdolności.
Podstawowe biblioteki Java, takie jak
ExecutorService
anuluj zadania asynchroniczne zinterrupt()
wywołaniami w wątku roboczym. Na przykład, jeśli zadanie zawiera jakąś pętlę, powinieneś sprawdzać status przerwania na każdej iteracji. Jeśli zadaniem jest wykonywanie operacji we / wy, powinny one również być przerywane - a konfiguracja może być trudna. W każdym razie pamiętaj, że kod musi aktywnie sprawdzać przerwania; ustawienie przerwania niekoniecznie nic nie robi.Oczywiście, jeśli twoim zadaniem jest jakaś prosta pętla, możesz po prostu sprawdzić aktualny czas na każdej iteracji i zrezygnować po upływie określonego czasu. W takim przypadku wątek roboczy nie jest potrzebny.
źródło
execute()
Metoda interfejsu nadrzędnegoExecutor
może uruchomić zadanie w wątku wywołującym. Nie ma podobnego stwierdzenia dlasubmit()
metodExecutorService
tych zwracanychFuture
instancji. Implikacja usługi polega na tym, że istnieją wątki robocze, które muszą zostać wyczyszczone przez zamknięcie, a zadania są wykonywane asynchronicznie. To powiedziawszy, w umowie nie ma nic, co mówi, żeExecutorService
zabrania się wykonywania zadań w wątku zgłaszającym; gwarancje te pochodzą z interfejsów API implementacji, takich jakExecutors
fabryki.Rozważ użycie instancji klasy ExecutorService . Zarówno
invokeAll()
iinvokeAny()
są dostępne z metodytimeout
parametru.Bieżący wątek będzie blokowany do czasu zakończenia metody (nie jestem pewien, czy jest to pożądane), ponieważ zadanie (zadania) zakończyło się normalnie lub upłynął limit czasu. Możesz sprawdzić zwrócony
Future
(e) w celu ustalenia, co się stało.źródło
Zakładając, że kod wątku jest poza twoją kontrolą:
Z dokumentacji Java wspomnianej powyżej:
Dolna linia:
Upewnij się, że wszystkie wątki mogą zostać przerwane, w przeciwnym razie potrzebujesz konkretnej wiedzy na temat wątku - na przykład posiadania flagi do ustawienia. Być może możesz wymagać od ciebie zadania wraz z kodem potrzebnym do jego zatrzymania - zdefiniuj interfejs za pomocą
stop()
metody. Możesz także ostrzec, gdy nie udało Ci się zatrzymać zadania.źródło
BalusC powiedział:
Ale jeśli zastąpi
Thread.sleep(4000);
sięfor (int i = 0; i < 5E8; i++) {}
to nie skompilować, ponieważ pusta pętla nie rzucaćInterruptedException
.Aby nić mogła zostać przerwana, musi rzucić
InterruptedException
.Wydaje mi się to poważnym problemem. Nie widzę, jak dostosować tę odpowiedź do pracy z ogólnym długoterminowym zadaniem.
Edytowano, aby dodać: Ponownie zadałem to pytanie: [ przerywając wątek po ustalonym czasie, czy musi zgłosić wyjątek InterruptedException? ]
źródło
Myślę, że powinieneś przyjrzeć się odpowiednim mechanizmom obsługi współbieżności (wątki działające w nieskończonych pętlach same w sobie nie brzmią dobrze, btw). Przeczytaj trochę o temacie „zabijanie” lub „zatrzymywanie” wątków .
To, co opisujesz , brzmi jak „spotkanie”, więc możesz rzucić okiem na CyclicBarrier .
Mogą istnieć inne konstrukcje (np. Użycie CountDownLatch na przykład ), które mogą rozwiązać twój problem (jeden wątek czeka z limitem czasu na zatrzask, drugi powinien odliczać zatrzask, jeśli wykonał swoją pracę, co zwolniłoby twój pierwszy wątek albo po limit czasu lub wywołanie odliczania zatrzasku).
Zazwyczaj polecam dwie książki z tego zakresu: Programowanie współbieżne w Javie i Współbieżność Java w praktyce .
źródło
Właśnie temu stworzyłem klasę pomocników. Działa świetnie:
Nazywa się to tak:
źródło
Wysyłam ci fragment kodu, który pokazuje sposób rozwiązania problemu. Jako przykład czytam plik. Możesz użyć tej metody do innej operacji, ale musisz zaimplementować metodę kill (), aby główna operacja została przerwana.
mam nadzieję, że to pomoże
pozdrowienia
źródło
Oto moja bardzo prosta w użyciu klasa pomocnicza do uruchomienia lub wywołania fragmentu kodu Java :-)
Opiera się to na doskonałą odpowiedź od BalusC
źródło
Poniższy fragment kodu rozpocznie operację w osobnym wątku, a następnie zaczeka do 10 sekund na zakończenie operacji. Jeśli operacja nie zakończy się na czas, kod podejmie próbę anulowania operacji, a następnie kontynuuje na swój wesoły sposób. Nawet jeśli operacji nie można łatwo anulować, wątek nadrzędny nie będzie czekał na zakończenie wątku podrzędnego.
getExecutorService()
Metoda może być realizowana na wiele sposobów. Jeśli nie masz żadnych szczególnych wymagań, możesz po prostu wezwaćExecutors.newCachedThreadPool()
do pulowania wątków bez górnego limitu liczby wątków.źródło
SomeClass
iFuture
?Jednej rzeczy, o której nie wspomniałem, jest to, że zabijanie wątków jest ogólnie złym pomysłem. Istnieją techniki tworzenia metod gwintowanych mogą zostać przerwane , ale różni się to od zabicia wątku po upływie określonego czasu.
Ryzyko związane z tym, co sugerujesz, polega na tym, że prawdopodobnie nie wiesz, w jakim stanie będzie wątek, gdy go zabijesz - więc ryzykujesz wprowadzeniem niestabilności. Lepszym rozwiązaniem jest upewnienie się, że kod wątkowy albo się nie zawiesi, albo ładnie odpowie na żądanie przerwania.
źródło
Świetna odpowiedź BalusC:
ale aby dodać, że sam limit czasu nie przerywa samego wątku. nawet jeśli sprawdzasz za pomocą while (! Thread.interrupt ()) w swoim zadaniu. jeśli chcesz upewnić się, że wątek został zatrzymany, powinieneś także upewnić się, że future.cancel () jest wywoływany, gdy wyjątek limitu czasu jest wyłapany.
źródło
Myślę, że odpowiedź zależy głównie od samego zadania.
Jeśli pierwsza odpowiedź brzmi „tak”, a druga „nie”, możesz uprościć to tak:
Jeśli nie jest to opcja, zawęź wymagania - lub pokaż kod.
źródło
Szukałem usługi Executor, która może przerwać wszystkie przekroczone limity czasu Runnables przez nią wykonane, ale nie znalazłem żadnej. Po kilku godzinach stworzyłem taki jak poniżej. Tę klasę można zmodyfikować w celu zwiększenia odporności.
Stosowanie:
źródło
Teraz spotykam taki problem. Zdarza się zdekodować obraz. Proces dekodowania zajmuje zbyt dużo czasu, aby ekran pozostawał czarny. Dodam kontroler czasu: kiedy czas jest zbyt długi, wyskakuj z bieżącego wątku. Oto diff:
źródło
Miałem ten sam problem. Więc wpadłem na takie proste rozwiązanie.
Gwarantuje, że jeśli blok nie zostanie wykonany w wyznaczonym terminie. proces zostanie zakończony i zgłosi wyjątek.
przykład:
źródło
W rozwiązaniu podanym przez BalusC główny wątek pozostanie zablokowany na czas oczekiwania. Jeśli masz pulę wątków z więcej niż jednym wątkiem, potrzebujesz tej samej liczby dodatkowych wątków, które będą korzystać z Future.get (długi limit czasu, jednostka TimeUnit) blokującego wywołanie, aby czekać i zamykać wątek, jeśli przekroczy on limit czasu.
Ogólnym rozwiązaniem tego problemu jest utworzenie programu ThreadPoolExecutor Decorator, który może dodać funkcję limitu czasu. Ta klasa Dekoratora powinna tworzyć tyle wątków, ile ma ThreadPoolExecutor, a wszystkie te wątki powinny być używane tylko do oczekiwania i zamknięcia ThreadPoolExecutor.
Klasę ogólną należy zaimplementować jak poniżej:
Powyższy dekorator może być używany jak poniżej:
źródło