Win Api- wątki, priorytety, wątek główny

0

Cześć!
Napisałem sobie program, który:
Tworzy jakieś cztery wątki, przekazuje im po jednym argumencie (jakaś zmienna globalna) i po przeliczeniu do określonej liczby inkrementuje zmienną. W mainie wartości n1-n4 są wypisywane. Wszędzie są pętle nieskończone, poza mainem gdzie jest if(_kbhit()){3x getch, 'a' zawiesza wątki, 'z' wznawia a 'esc' kończy program}.

Chciałem zmienić priorytety wątków, tak żeby jeden był wysoki, a reszta niskie. Wtedy przecież powinno wykonywać program wątku o najwyższym priorytecie dopóki się on nie skończy? Więc powinno mi zwiększać tylko jedną zmienną, a cały czas idzie w takim samym tempie wszystko. [Kod dam niżej].

Pytanie 1: Myślałem, żeby stworzyć główny wątek przez CreateProcess i żeby to odpalało konsolę w której byłyby wyświetlane te zmienne. Chcąc żeby tamten wątek (główny) się konczył na klawisz esc (analogicznie jak wyżej, tylko zamiast w main'ie programu zrobić to w głównym wątku) to co trzeba zrobić? Stworzyć nowy typ wątku i tam to wszystko wrzucić?

Pytanie 2: Czy można przekazać większą ilość parametru do wątku? Np. numer wątku jako liczbę porządkową i zmienną do inkrementacji?

 	HANDLE hw_1 = CreateThread(0, 0, watek, &n1, 0, 0);
	SetThreadPriority(hw_1, THREAD_PRIORITY_TIME_CRITICAL);
	HANDLE hw_2 = CreateThread(0, 0, watek, &n2, 0, 0);
	SetThreadPriority(hw_2, THREAD_PRIORITY_IDLE);
	HANDLE hw_3 = CreateThread(0, 0, watek, &n3, 0, 0);
	SetThreadPriority(hw_3, THREAD_PRIORITY_IDLE);
	HANDLE hw_4 = CreateThread(0, 0, watek, &n4, 0, 0);
	SetThreadPriority(hw_4, THREAD_PRIORITY_IDLE);
	while (1) {
		if (_kbhit()) {
			char c = _getch();
			if (c == 'a') {
				SuspendThread(hw_1);
				SuspendThread(hw_2);
				SuspendThread(hw_3);
				SuspendThread(hw_4);
			}
			else if (c == 'z') {
				ResumeThread(hw_1);
				ResumeThread(hw_2);
				ResumeThread(hw_3);
				ResumeThread(hw_4);
			}
			else if (c == 27)
				break;
		}
		Sleep(500);
		printf("n1: %d\nn2: %d\nn3: %d\nn4: %d\n\n", n1, n2, n3, n4);
	}
	TerminateThread(hw_1, 0);
	TerminateThread(hw_2, 0);
	TerminateThread(hw_3, 0);
	TerminateThread(hw_4, 0);
	CloseHandle(hw_1);
	CloseHandle(hw_2);
	CloseHandle(hw_3);
	CloseHandle(hw_4);
	HANDLE handle;
	handle = OpenProcess(PROCESS_ALL_ACCESS, 0, GetCurrentProcessId());
	TerminateProcess(handle, 0);
0

możesz spakować do klasy kilka zmiennych i podawać obiekt do wątku

1

Chciałem zmienić priorytety wątków, tak żeby jeden był wysoki, a reszta niskie. Wtedy przecież powinno wykonywać program wątku o najwyższym priorytecie dopóki się on nie skończy?

Skąd takie założenie? Zwłaszcza na wielordzeniowym procesorze masz praktycznie gwarancję, że tak nie będzie. Jeden rdzeń będzie przez większość czasu zajęty przez wątek time critical, ale reszta będzie wolna dla pozostałych.

Myślałem, żeby stworzyć główny wątek przez CreateProcess

CreateProcess tworzy całkiem nowy proces, a nie wątek. To znaczy że w nowym procesie tych zmiennych nie będzie, będą zupełnie nowe.

Czy można przekazać większą ilość parametru do wątku? Np. numer wątku jako liczbę porządkową i zmienną do inkrementacji?

Można przekazać wskaźnik do struktury. Pamiętaj że jeśli jeden wątek coś zapisuje by drugi to odczytał, potrzebne są metody typu InterlockedExchange i podobne.
A nawet jeśli można zagwarantować atomowość danej operacji bez nich, to wtedy potrzebne jest volatile przy zmiennej.

Pokaż cały kod - z funkcją watek().

W normalnym programie unikaj TerminateThread(). Wątek powinien się zakończyć od wewnątrz.

0

Skąd takie założenie? Zwłaszcza na wielordzeniowym procesorze masz praktycznie gwarancję, że tak nie będzie. Jeden rdzeń będzie przez większość czasu zajęty przez wątek time critical, ale reszta będzie wolna dla pozostałych.

No tak, masz rację, o tym nie pomyślałem. Tak samo jak o istnieniu Balance Set Menager, który i tak chroniłby pozostałe wątki przed zagłodzeniem.

CreateProcess tworzy całkiem nowy proces, a nie wątek. To znaczy że w nowym procesie tych zmiennych nie będzie, będą zupełnie nowe.

No właśnie, więc dlatego pytałem jak zrobić, żebym mógł stworzyć nowy proces który by te wątki obsługiwał. Chodzi mi o to, że chcę zrozumieć po co w praktyce może być funkcja CreateProcess(); i próbuję sobie tworzyć różne programy do napisania o tyle o ile.

Można przekazać wskaźnik do struktury. Pamiętaj że jeśli jeden wątek coś zapisuje by drugi to odczytał, potrzebne są metody typu InterlockedExchange i podobne.
A nawet jeśli można zagwarantować atomowość danej operacji bez nich, to wtedy potrzebne jest volatile przy zmiennej.

To jeszcze nazbyt do przodu, atomowe zmienne będą ale jeszcze nie teraz :).

W normalnym programie unikaj TerminateThread(). Wątek powinien się zakończyć od wewnątrz.

Więc mógłbym przerzucić getch'a do wątku i jezeli jest "esc" to tam dawać Terminate Thread()? To byłoby lepsze (i działające?) rozwiązanie?

Pokaż cały kod - z funkcją watek().

 #include "stdafx.h"
#include <Windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <tchar.h>
#include <Psapi.h>

int n1 = 0;
int n2 = 0;
int n3 = 0;
int n4 = 0;

//TCHAR cmd[50] = L"cmd.exe";
unsigned long __stdcall watek(void* Param) {
	while (1) {
		for (int i = 0; i < 30000; i++);
		(*(int*)Param)++;
	}
	return 0;
}

int main()
{
	/*PROCESS_INFORMATION pi;
	STARTUPINFO si;

	ZeroMemory(&pi, sizeof(pi));
	ZeroMemory(&si, sizeof(si));
	si.cb = sizeof(si);
	if (!CreateProcess(NULL,
		cmd,
		NULL,
		NULL,
		FALSE,
		0,
		NULL,
		NULL,
		&si,
		&pi))
	{
		printf("Could not create process. (%ld)", GetLastError());
		return 1;
	}
	THREAD_PRIORITY_TIME_CRITICAL
	THREAD_PRIORITY_HIGHEST
	THREAD_PRIORITY_ABOVE_NORMAL
	THREAD_PRIORITY_NORMAL
	THREAD_PRIORITY_BELOW_NORMAL
	THREAD_PRIORITY_LOWEST
	THREAD_PRIORITY_IDLE*/

	HANDLE hw_1 = CreateThread(0, 0, watek, &n1, 0, 0);
	SetThreadPriority(hw_1, THREAD_PRIORITY_TIME_CRITICAL);
	HANDLE hw_2 = CreateThread(0, 0, watek, &n2, 0, 0);
	SetThreadPriority(hw_2, THREAD_PRIORITY_IDLE);
	HANDLE hw_3 = CreateThread(0, 0, watek, &n3, 0, 0);
	SetThreadPriority(hw_3, THREAD_PRIORITY_IDLE);
	HANDLE hw_4 = CreateThread(0, 0, watek, &n4, 0, 0);
	SetThreadPriority(hw_4, THREAD_PRIORITY_IDLE);
	while (1) {
		if (_kbhit()) {
			char c = _getch();
			if (c == 'a') {
				SuspendThread(hw_1);
				SuspendThread(hw_2);
				SuspendThread(hw_3);
				SuspendThread(hw_4);
			}
			else if (c == 'z') {
				ResumeThread(hw_1);
				ResumeThread(hw_2);
				ResumeThread(hw_3);
				ResumeThread(hw_4);
			}
			else if (c == 27)
				break;
		}
		Sleep(500);
		printf("n1: %d\nn2: %d\nn3: %d\nn4: %d\n\n", n1, n2, n3, n4);
	}

	TerminateThread(hw_1, 0);
	TerminateThread(hw_2, 0);
	TerminateThread(hw_3, 0);
	TerminateThread(hw_4, 0);
	CloseHandle(hw_1);
	CloseHandle(hw_2);
	CloseHandle(hw_3);
	CloseHandle(hw_4);
	HANDLE handle;
	handle = OpenProcess(PROCESS_ALL_ACCESS, 0, GetCurrentProcessId());
	TerminateProcess(handle, 0);
	return 0;
}
1

No właśnie, więc dlatego pytałem jak zrobić, żebym mógł stworzyć nowy proces który by te wątki obsługiwał.

Nowy proces to nowy proces (program exe). Nie przenosi się wątków z jednego procesu do drugiego.
Jest możliwa komunikacja międzyprocesorowa, ale na razie zostaw to w spokoju.

Chodzi mi o to, że chcę zrozumieć po co w praktyce może być funkcja CreateProcess()

Żeby uruchomić nowy program.

To jeszcze nazbyt do przodu, atomowe zmienne będą ale jeszcze nie teraz :)

Nie ma "nie teraz". Robisz wątki, to ich synchronizacja musi być zrobiona prawidłowo.
Od razu, nie kiedyś tam. Wątki łatwe nie są.

Więc mógłbym przerzucić getch'a do wątku i jezeli jest "esc" to tam dawać Terminate Thread()? To byłoby lepsze (i działające?) rozwiązanie?

Raczej getcha w wątku głównym, potem zmiana jakiejś zmiennej np. volatile bool koniec, i okresowe jej sprawdzanie w pozostałych wątkach.

0
#include "stdafx.h"
#include <Windows.h>
#include <mmsystem.h>
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <tchar.h>
#include <Psapi.h>
volatile DWORD end;
int n1 = 0;
int n2 = 0;
int n3 = 0;
int n4 = 0;
struct param {
	const int number;
	int& n;
};
unsigned long __stdcall watek(void* Param) {
	DWORD kill = 0;
	while (1) {
		for (int i = 0; i < 30000; i++);
		(*(struct param*)Param).n++;
		InterlockedExchange(&kill, end);
		if (kill) {
			break;
		}
	}
	printf("Thread number %u terminate!\n", (*(struct param*)Param).number);
	TerminateThread(OpenProcess(PROCESS_TERMINATE, 0, GetCurrentThreadId()), 0);
	return 0;
}

int main()
{
	end = 0;
	struct param p = { 1, n1 };
	struct param p2 = { 2, n2 };
	struct param p3 = { 3, n3 };
	struct param p4 = { 4, n4 };
	HANDLE hw_1 = CreateThread(0, 0, watek, &p, 0, 0);
	SetThreadPriority(hw_1, THREAD_PRIORITY_TIME_CRITICAL);
	HANDLE hw_2 = CreateThread(0, 0, watek, &p2, 0, 0);
	SetThreadPriority(hw_2, THREAD_PRIORITY_IDLE);
	HANDLE hw_3 = CreateThread(0, 0, watek, &p3, 0, 0);
	SetThreadPriority(hw_3, THREAD_PRIORITY_IDLE);
	HANDLE hw_4 = CreateThread(0, 0, watek, &p4, 0, 0);
	SetThreadPriority(hw_4, THREAD_PRIORITY_IDLE);
	while (1) {
		if (_kbhit()) {
			char c = _getch();
			if (c == 'a') {
				SuspendThread(hw_1);
				SuspendThread(hw_2);
				SuspendThread(hw_3);
				SuspendThread(hw_4);
				printf("* * *All threads are suspended! * * *\n");
			}
			else if (c == 'z') {
				ResumeThread(hw_1);
				ResumeThread(hw_2);
				ResumeThread(hw_3);
				ResumeThread(hw_4);
				printf("* * *All threads are resumed! * * *\n");
			}
			else if (c == 27) {
				end = 1;
				break;
			}
		}
		Sleep(500);
		printf("n1: %d\nn2: %d\nn3: %d\nn4: %d\n\n", n1, n2, n3, n4);
	}
	WaitForSingleObject(hw_1, INFINITE);
	WaitForSingleObject(hw_2, INFINITE);
	WaitForSingleObject(hw_3, INFINITE);
	WaitForSingleObject(hw_4, INFINITE);
	CloseHandle(hw_1);
	CloseHandle(hw_2);
	CloseHandle(hw_3);
	CloseHandle(hw_4);
	HANDLE handle;
	handle = OpenProcess(PROCESS_ALL_ACCESS, 0, GetCurrentProcessId());
	TerminateProcess(handle, 0);
	return 0;
}

W ten sposób? Poprawnie rozumiem zasadę działania? Volatile daje to że nie jest sprawdzana wartość tylko rejestry przez co bierzemy dokładną wartość w danej chwili. Jak wcisnę ESC to zmieniam end = 1 i w wątku jest sprawdzana ta wartość- jeżeli jest 1 to przerywa pętlę nieskończoną, daje komunikat o zabiciu wątku i kończy wątek- program główny czeka na zakończenie. Potem dopiero zamyka uchwyty.

Jeżeli jest ok to jeszcze pytania:
Pytanie 1: Można jakoś sprawdzać czas oczekiwania na zasoby danego wątku?
Pytanie 2: Jakieś zadanie z funkcją timeGetTime()? Do czego ona się przydaje? Bo poza tym że zwraca czas w ms od momentu uruchomienia systemu i ma dokładkość do 1ms to nie wiem zbytnio po co to jest.
Pytanie 3: Chciałbym stworzyć strukturę z numerem wątku i tą liczbą n globalną- żeby przekazać do wątku. W jaki sposób to stworzyć? Struktura będzie:

struct param{
int number;
int& n;
};

czyli tak jak w kodzie powyżej?

0
    TerminateThread(OpenProcess(PROCESS_TERMINATE, 0, GetCurrentThreadId()), 0);

Wyrzuć. Niech się funkcja po prostu zakończy.

    HANDLE handle;
    handle = OpenProcess(PROCESS_ALL_ACCESS, 0, GetCurrentProcessId());
    TerminateProcess(handle, 0);

Jak wyżej.

Volatile daje to że nie jest sprawdzana wartość tylko rejestry przez co bierzemy dokładną wartość w danej chwili.

Nie. A nawet odwrotnie. volatile powoduje, że każdy zapis i odczyt zmiennej jest dokonywany w pamięci, i nie jest opóźniany ani pomijany.

 Struktura będzie:

struct param{
int number;
int& n;
};

ta referencja to chyba nie jest dobry pomysł. pozbądź się tych n1, n2 i zostaw tylko struktury.

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