C++ <thread> Mój program wysypuje się

0

Witam, próbuje napisać program do automatycznego wyłączania komputera o konkretnej godzinie. Pętlę sprawdzającą porównującą aktualną godzinę do zaplanowanej wrzuciłem do osobnego wątku. I gdy program uruchamia osobny wątek to się wysypuje. (jeżeli uruchomię funkcje w tym samym wątku co main() wszystko działa. Nie mam błędów kompilacji.

//funkcja main();
//reszta kodu
switch (zm)
{
case '1':
{
	automatyzacja(1, 0, 0);
	break;
} 
 

bool tryb_automat = 1;

void automatyzacja(bool m, int aGodz, int aMin)
{
	//if (m == 1) tryb_automat = 1;
	//if (m == 0) tryb_automat = 0;

	if (tryb_automat == 1 && aGodz == 0 && aMin == 0)
	{
		cout << "\n\nPodaj o której godzinie chcesz wyłączyć komputer:\n";
	GODZINA:
		cout << "Godzina: ";
		cin >> Godz;
		if (Godz <= 0 || Godz > 23)
		{
			SetConsoleTextAttribute(hOut, FOREGROUND_RED | FOREGROUND_INTENSITY);
			cout << "\a\nWpisałeś niepoprawną godzinę! Spróbuj ponownie!\n";
			SetConsoleTextAttribute(hOut, FOREGROUND_GREEN | FOREGROUND_INTENSITY);
			goto GODZINA;
		}

	MINUTY:
		cout << "\nMinut: ";
		cin >> Min;
		if (Min <= 0 || Min >= 60)
		{
			SetConsoleTextAttribute(hOut, FOREGROUND_RED | FOREGROUND_INTENSITY);
			cout << "\a\nWpisałeś niepoprawną ilość minut!!! Spróbuj ponownie!\n";
			SetConsoleTextAttribute(hOut, FOREGROUND_GREEN | FOREGROUND_INTENSITY);
			goto MINUTY;
		}
		//cout << "\n\naGodz = " << aGodz << "\t\taMin = " << aMin;
		cout << "\nGotowe! Komputer wyłączy się o " << Godz << ":" << Min << endl;
	}
	thread wylacznik1(automat, aGodz, aMin); //tylko z tym nie działa
	//wylacznik1.join(); //działa
}

void automat(int aGodz, int aMin)
{
	while (tryb_automat == 1)
	{
		if (aGodz >= 0 && aGodz < 24 && aMin >= 0 && aMin <= 60)
		{
			update_czas();
			if (godzina == aGodz && minuta == aMin)
			{
				system("shutdown -s -f");
			}
		}
		Sleep(15000);
	}
}

Chcę aby funkcja automat() działała w tle gdyż w przyszłości cichłem dodać możliwość ustawienia kilku godzin przy których komputer będzie się wyłączać a oprócz tego chcę używać innych opcji programu więc nie mogę użyć wylacznik1.join();
Czy jest jakiś sposób aby ten osobny wątek działał bez używania funkcji join();?
Z góry dziękuję za odpowiedź.

0

Niepotrzebne ci pętla i wątek, możesz użyć SetTimer.

0

Funkcja niby fajna, ale nie umiem zmusić jej aby działała w tle, w dodatku nie wiem jak ją zaimplementować do mojego kodu powyżej. Czy mógłbyś mi z tym jakoś pomóc? Pozdrawiam.

0

Dwie sprawy:

  1. Program z wątkiem ci się crashuje, bo najprawdopodobniej nie czekasz w wątku głównym na zakończenie pracy wątku pobocznego. Tutaj warto dodać, że na tę chwilę tworzysz wątek w pewnym scopie i po wyjściu z tego scopa wątek jest po prostu detachowany. Innymi słowy nie możesz już go joinować.

  2. Po co piszesz taki program jak mógłbyś napisać prosty batch?

shutdown -s -t TUTAJ_RÓŻNICA_W_SEK_POMIĘDZY_CZASEM_ZAMKNIĘCIA_OS_A_AKTUALNYM_CZASEM 

Ewentualnie zrób tak jak napisał @Azarien, tj użyj timera.

0

Funkcja niby fajna, ale nie umiem zmusić jej aby działała w tle

najpierw zapytam: czego potrzebujesz na pierwszym planie, że potrzebne ci "tło"?

przypomniało mi się, ze SetTimer działa w oparciu o windowsową pętlę komunikatów, a w programie konsolowym jej zwykle nie ma - oczywiście można dodać, ale jeśli ma nie blokować "normalnego" konsolowego kodu, to musiałaby być mimo wszystko w osobnym wątku...

...chyba że cały program działałby w oparciu o pętlę komunikatów, ale musiałbyś pokazać więcej kodu.

0

@Satirev,

  1. to w takim razie jaki kod może działać wątku w tle?, właśnie Named Pipe chodzi mi przez cały okres działania programu z powodzeniem w tle...
 HANDLE hPipe;
	char buffer[1024];
	DWORD dwRead;

	hPipe = CreateNamedPipe(TEXT("\\\\.\\pipe\\Pipe"),
		PIPE_ACCESS_DUPLEX | PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,
		PIPE_WAIT,
		1,
		1024 * 16,
		1024 * 16,
		NMPWAIT_USE_DEFAULT_WAIT,
		NULL);
	while (hPipe != NULL)
	{
		if (ConnectNamedPipe(hPipe, NULL) != FALSE)
		{
			while (ReadFile(hPipe, buffer, sizeof(buffer), &dwRead, NULL) != FALSE)
			{
				if (widocznosc == 0) ukrywanie(1);
			}
		}

		DisconnectNamedPipe(hPipe);
	}
  1. Nie mogę od razu użyć shutdowna gdyż nie da się ustawić 2 lub więcej zadań na raz (np zamknięcie o 8:45 i o 15:10 czy zamknięcie o 10 i wylogowywanie o 12) Tu dopowiem że planuje zrobić aby program uruchamiał się przy starcie systemu i wykonywał zaplanowane czynności o konkretnej godzinie.

@Azarien tak jak pisałem wyżej potrzebuje w tle mieć zaplanowanie kilka operacji, w tle z powodzeniem chodzi też (jak wyżej) Named Pipe i dodatkowy void zczytujący polecenia z lini poleceń.... Dodatkowo zawsze chcę mieć możliwość dodawania / modyfikowania zaplanowanych zadań.

0

W zasadzie, na pierwszym planie póki co jest pętla wyświetlająca menu programu i switch uruchamiający opcje wyłączania / restartowania / wylogowywania (+ te opcje wzbogacone o wybór dokładnego czasu)
Do tego funkcja wyświetlająca "O programie" i wyłączanie programu.
[Na ten czas nie umiem programować skomplikowanych programów... dopiero się uczę. Jestem samoukiem...]

0
km2442 napisał(a):
  1. to w takim razie jaki kod może działać wątku w tle?

Każdy tylko musi być poprawnie napisany.

km2442 napisał(a):
  1. Nie mogę od razu użyć shutdowna gdyż nie da się ustawić 2 lub więcej zadań na raz (np zamknięcie o 8:45 i o 15:10 czy zamknięcie o 10 i wylogowywanie o 12) Tu dopowiem że planuje zrobić aby program uruchamiał się przy starcie systemu i wykonywał zaplanowane czynności o konkretnej godzinie.

Skoro możesz mieć kilka akcji (w tym reboot/shutdown) tzn. że twój program musi mieć jakiś local storage. Zatem równie dobrze mógłbyś to robić z poziomu skryptu batch/python/powershell/whatever.

W każdym razie skoro używasz named pipeów jako IPC to pewnie masz bardziej skomplikowaną architekturę. W takim wypadku użyj jakiegoś frameworka do timerów np. boost::asio (timer) albo napisz własny wrapper na std::thread/std::async emulujący timery.

0
km2442 napisał(a):

Na ten czas nie umiem programować skomplikowanych programów... dopiero się uczę. Jestem samoukiem...

Tak jak napisałem wyżej, nie jestem jakimś mistrzem programowania tylko typowym n00bem... (C++ uczę się z ksiazki "Symfonia C++ Standard")
A co do tego Named Pipe, przepisałem go z przykładu z internetu aby dało się uruchomić tylko jedną instancję tego programu na raz (w miarę rozwijania programu dodam może jakieś inne funkcje named pipe jeśli mi się uda.)

Wracając do biblioteki <thread> Po maksymalnym uproszczeniu kodu doszedłem do wniosku:

 #include <iostream> 
#include <conio.h> 
#include <thread> 
#include <Windows.h>

using namespace std;

void t2() { *dowolny kod lub brak kodu* }

void t1() { thread test(t2); }

int main() { 
t1(); //wysypuje się  

thread test(t2); //działa 

_getch(); }
1

Musisz jawnie wywołać detach() na tych wątkach (z tym, że na 100% nie powinieneś tak robić).

0

@Satirev wiem, że nie powinienem detachować wątków ale takie rozwiązanie dla mnie działa. Dzięki za pomysł :D

1

Działa, bo jak napisano wyżej, jeśli na obiekcie std::thread przed destrukcją nie zawołano .join() lub .detach() (joinable() == true), wtedy wołany jest terminate (http://www.cplusplus.com/reference/thread/thread/~thread/). Nie wiem do końca co ma robić Twój kod, jednak używanie detach() wcale nie jest czymś złym, wszystko zależy od tego co chcesz zrobić. Dodatkowo, należy pamiętać, że wystąpienie wyjątku pomiędzy stworzeniem obiektu std::thread i końcem zakresu (out of scope) najprawdopodobniej spowoduje ten sam błąd, nawet jeśli wyjątek łapiesz wyżej. Warto dlatego napisać sobie jakiś handler RAII na obiekt std::thread.

0

Dzięki za sugestie :)

1 użytkowników online, w tym zalogowanych: 0, gości: 1