Programowanie wielowątkowe Jarosław Kuchta Programowanie wielowątkowe
Procesy i wątki w systemie Windows Windows jest systemem wielowątkowym. Każdy proces ma przynajmniej jeden wątek, chociaż może mieć wiele wątków. Start programu, to start głównego wątku procesu. Główny wątek może uruchomić inny, poboczny wątek. Zamknięcie programu, to zamknięcie wszystkich wątków procesu.
Programowanie wielowątkowe w języku C Start programu: main(), wmain() – w programie konsoli WinMain(), wWinMain() – w programie okienkowym Procedura osobnego wątku: void ThreadProc(void *param) { … _endthread(); // zakończenie wątku }
Funkcje sterujące wątkami zadeklarowane w process.h: _beginthread – rozpoczęcie wątku _beginthreadex – rozszerzone rozpoczęcie wątku _endthread – zakończenie wątku _endthreadex – rozszerzone zakończenie wątku
Przykład – funkcja wątku void ThreadProc(void *param) { // ten wątek po prostu wypisuje parametr int h=*((int*)param); printf("%d Thread is Running!\n",h); _endthread(); }
Przykład – funkcja główna int main() { int n; int i; int val = 0; HANDLE handle; printf("\t Thread Demo\n"); printf("Enter the number of threads : "); scanf("%d",&n); for(i=1;i<=n;i++) val = i; handle = (HANDLE) _beginthread( ThreadProc,0,&val); // utworzenie wątku WaitForSingleObject(handle,INFINITE); // oczekiwanie na zakończenie wątku } return 0;
Programowanie wielowątkowe w MFC Hierarchia klas MFC: CObject – obiekt abstrakcyjny CCmdTarget – obiekt aktywny CWinThread – wątek Windows CWinApp – aplikacja Windows Aplikacja Windows jest wątkiem! Dwa rodzaje wątków: User Interface – wątek oparty o okna Worker – wątek pracujący w tle
Użyteczne dane m_nThreadID – identyfikator bieżącego wątku m_hThread – uchwyt bieżącego wątku m_bAutoDelete – czy wątek ma się sam usunąć po zamknięciu?
Funkcje wątkowe Funkcje globalne: Metody obiektowe: AfxBeginThread – utworzenie wątku AfxEndThread – zamknięcie wątku Metody obiektowe: CreateThread – utworzenie i wystartowanie wątku SuspendThread – zawieszenie wątku (inkrementacja licznika zawieszeń) ResumeThread – wznowienie wątku (dekrementacja licznika zawieszeń) SetThreadPriority – ustawienie priorytetu wątku GetThreadPriority – pobranie priorytetu wątku
Priorytety wątków THREAD_PRIORITY_HIGHEST THREAD_PRIORITY_ABOVE_NORMAL THREAD_PRIORITY_NORMAL THREAD_PRIORITY_BELOW_NORMAL THREAD_PRIORITY_IDLE
Przykład z użyciem funkcji Afx CwinThread *pThread = AfxBeginThread( ThreadFunction, &data); UINT ThreadFunction(LPVOID param) { DWORD result =0 ; // do somthig AfxEndThread(exitCode); return result; }
Kończenie wątku funkcja TerminateThread() funkcja ExitThread() instrukcja return – zalecana, pozostałe nie czyszczą stosu.
Programowanie wielowątkowe w C# (1) // współdzielona zmienna private string _threadOutput = ""; // funkcja pierwszego wątku void DisplayThread1() { while (_stopThreads == false) { Console.WriteLine("Display Thread 1"); _threadOutput = "Hello Thread1"; Thread.Sleep(1000); // symulacja działania pierwszego wątku Console.WriteLine("Thread 1 Output --> {0}", _threadOutput); } }
Programowanie wielowątkowe w C# (2) // funkcja drugiego wątku void DisplayThread2() { while (_stopThreads == false) { Console.WriteLine("Display Thread 2"); _threadOutput = "Hello Thread2"; Thread.Sleep(1000); // symulacja działania drugiego wątku Console.WriteLine("Thread 2 Output --> {0}", _threadOutput); }
Programowanie wielowątkowe w C# - inicjacja wątków Class1() { // utworzenie dwóch wątków Thread thread1 = new Thread(new ThreadStart(DisplayThread1)); Thread thread2 = new Thread(new ThreadStart(DisplayThread2)); // wystartowanie wątków thread1.Start(); thread2.Start(); }
Wynik Współdzielona zmienna jest asynchronicznie nadpisywana
Synchronizacja przez lock (1) // współdzielona zmienna private string _threadOutput = ""; // funkcja pierwszego wątku void DisplayThread1() { while (_stopThreads == false) { lock (this) // blokada na własnej instancji { Console.WriteLine("Display Thread 1"); _threadOutput = "Hello Thread1"; Thread.Sleep(1000); // symulacja działania pierwszego wątku Console.WriteLine("Thread 1 Output --> {0}", _threadOutput); }
Synchronizacja przez lock (2) // funkcja drugiego wątku void DisplayThread2() { while (_stopThreads == false) { lock(this) // blokada na własnej instancji { Console.WriteLine("Display Thread 2"); _threadOutput = "Hello Thread2"; Thread.Sleep(1000); // symulacja działania drugiego wątku Console.WriteLine("Thread 2 Output --> {0}", _threadOutput); } } }
Wynik (po synchronizacji)
Synchronizacja przez AutoResetEvent (1) // zmienne sygnałowe do wzajemnego odblokowywania AutoResetEvent _blockThread1 = new AutoResetEvent(false); AutoResetEvent _blockThread2 = new AutoResetEvent(true); void DisplayThread_1() { while (_stopThreads == false) { // pierwszy wątek czeka, gdy drugi działa _blockThread1.WaitOne(); // po wołaniu Set przez drugi wątek, pierwszy kontynuuje działanie Console.WriteLine("Display Thread 1"); _threadOutput = "Hello Thread 1"; Thread.Sleep(1000); // symulacja działania pierwszego wątku Console.WriteLine("Thread 1 Output --> {0}", _threadOutput); // zakończenie działania – odblokowanie drugiego wątku _blockThread2.Set(); }
Synchronizacja przez AutoResetEvent (2) void DisplayThread_2() { while (_stopThreads == false) { // drugi wątek czeka, gdy pierwszy działa _blockThread2.WaitOne(); // po wołaniu Set przez pierwszy wątek, drugi kontynuuje działanie Console.WriteLine("Display Thread 2"); _threadOutput = "Hello Thread 2"; Thread.Sleep(1000); // symulacja działania drugiego wątku Console.WriteLine("Thread 2 Output --> {0}", _threadOutput); // zakończenie działania – odblokowanie pierwszego wątku _blockThread1.Set(); }