Wpis użytkownika qarmin w Programowanie
qarminTwórca
3piorunówMam ostatnio problemy z programem, który ubijam poleceniem timeout.
Program wykonuje setki(w zasadzie to grupowo robi 10000) operacji zapisu plików do określonego folderu z wątków rayona(rust) i wygląda na to, że bez względu czy ubijam go sygnałem TERM czy KILL, to nieco później (0-10s) po zabiciu programu, nie mogę usunąć całego folderu z plikami, bo wygląda, że program ciągle w tle tworzy nowe pliki, więc próba usunięcia takiego katalogu przez "rm -rf" wypisuje błąd "rm: cannot remove '/opt/tmp_folder/short_normal_1/16474004021118382402': Directory not empty"
Zatem by rozwiązać problem przerzucam timer końca działania do programu zamiast ubijać program z zewnątrz.
Jednak mam tutaj ponownie zagwozdkę.
Mam dwie koncepcje
Pierwsza to taka, że pierwszy wątek który złapie problem, to przerywa cały program:
>fn check_for_exit() {
> if time_left < 0 {
> process::exit(127);
> }
>}
>
>
>files_chunks.into_par_iter().for_each(|| {
> check_for_exit();
>
> for file in files_chunks {
> fs::copy("file", output_dir);
> }
>});
Druga to taka, że czekam aż wszystkie wątki się skończą i dopiero wtedy przerywam wykonywanie programu
fn check_for_exit() -> bool {
return time_left < 0;
}
files_chunks.into_par_iter().map(|| {
if check_for_exit() {
return None;
}
for file in files_chunks {
fs::copy("file", output_dir);
}
Some(())
}).while_some().collect<()>();
if check_for_exit() {
process::exit(127);
}
Niby punkt drugi bezpieczniejszy, ale punkt pierwszy też przecież przecież powinien wszystkie wątki z kopiowaniem plików ubić. Dobrze kminię, czy jednak punkt pierwszy nie jest bezpieczny?
Komentarze (3)
@qarmin Analizując obie koncepcje, które przedstawiłeś, można zauważyć kilka istotnych różnic w sposobie zarządzania zakończeniem wątków i zatrzymaniem programu.
Pierwsza koncepcja
Zalety*:
* Każdy wątek sprawdza warunek time_left < 0 przed rozpoczęciem kopiowania.
* Jeśli warunek jest spełniony, natychmiast wywołuje process::exit(127), co natychmiastowo kończy cały program.
Wady*:
* process::exit(127) powoduje natychmiastowe zakończenie programu bez czekania na zakończenie pozostałych wątków. To może skutkować niekompletnym zakończeniem operacji IO, co może być przyczyną problemów z plikami.
* Możliwe nieprzewidywalne zachowanie, jeśli process::exit(127) jest wywoływane z wielu wątków jednocześnie.
Druga koncepcja
Zalety*:
* Sprawdza warunek time_left < 0 przed rozpoczęciem kopiowania w każdym wątku, ale zamiast natychmiastowego zakończenia, wątki, które spełniają warunek, po prostu kończą swoją pracę.
* Pozwala wszystkim aktywnym wątkom dokończyć swoje operacje kopiowania, zanim program sprawdzi, czy powinien zakończyć się process::exit(127).
* Bezpieczniejsze podejście, ponieważ nie powoduje natychmiastowego zakończenia programu, co pozwala na bardziej przewidywalne zarządzanie zasobami.
Wady*:
* Może powodować krótkie opóźnienie w zakończeniu programu, jeśli trzeba czekać na zakończenie wszystkich wątków.
Wnioski
Druga koncepcja jest bardziej bezpieczna i elegancka, ponieważ pozwala na kontrolowane zakończenie programu i uniknięcie problemów związanych z nieskończonym tworzeniem plików po wywołaniu timeout.
Natychmiastowe zakończenie programu przy użyciu process::exit w pierwszej koncepcji może prowadzić do nieprzewidywalnych problemów związanych z niedokończonymi operacjami IO. W drugiej koncepcji wątki mogą bezpiecznie zakończyć swoje zadania, co zmniejsza ryzyko wystąpienia problemów z plikami i zasobami.
Zatem rekomenduję skorzystanie z drugiej koncepcji. Jeśli jednak decydujesz się na pierwszą koncepcję, warto wprowadzić mechanizm, który upewni się, że wszystkie wątki zakończyły swoją pracę przed zamknięciem programu, aby uniknąć problemów z niekompletnym przetwarzaniem plików.
@qarmin a to nie jest kwestia tego, że operacje na plikach robi kernel? Ubicie procesu nie przerywa fs::copy.
Po drugie, obsługa sygnałów nie jest synchroniczna. Jak zrobisz kill PID && rm costam, to na pewno to nie zadziała. Musisz poczekać, aż proces obsłuży sygnał i się zamknie.
Jak robisz timeout na wątkach wewnątrz programu, to z pewnością da się to bardziej elegancko obsłużyć.
@qarmin Nie pisałem dawno w rust, zwłaszcza na tym poziomie, ale zdecydowanie druga opcja. Wydaje mi się, że w pierwszej opcji będziesz miał proces w kolejce do ubicia/ubity, a to co zostanie to będą tzw. detached threads. Ale nie jestem (już) ekspertem, podpytaj może kogoś innego :smiley: