SOISK - SYSTEMY OPERACYJNE I SIECI KOMPUTEROWE
Tomasz Puchała

LAB2

 

Instrukcja laboratoryjna nr 2
Synchronizacja zmiennymi globalnymi
oprac. Robert Tomaszewski
Program przykładowy ex.c
 
/* Przykład z wykładu - prosty program wielowątkowy demonstrujący zasadę tworzenia wątków
(tutaj 2 wątki potomne) przez program (wątek!) główny; zwróć uwagę na wartości
zmiennych licznikowych w trakcie działania programu:
licznik – zmienna globalna, używana przez wątek główny
licznik1 – zmienna statyczna w funkcji wątków, współdzielona przez wątki
licznik2 – zmienna automatyczna w funkcji wątków, prywatna dla każdego wątku
W przykładzie brak jakiejkolwiek synchronizacji i komunikacji między wątkami.
Brak również przekazywania parametrów do funkcji wątków.
*/
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
int licznik=0;
/* Funkcja wątku(ów) */
DWORD WINAPI proc(LPVOID arg)
{
static int licznik1=0;
int i,licznik2=0;
for (i=0;i<10;i++)
{
/* poniższa pętla inkrementuje oba liczniki, wyświetla ich wartości oraz identyfikator
aktualnie wykonywanego wątku */
++licznik1; ++licznik2;
printf("WATEK %u: licznik statyczny watku: %dn",GetCurrentThreadId(),licznik1);
printf("WATEK %u: licznik prywatny watku: %dn",GetCurrentThreadId(),licznik2);
}
}
int main(void)
{
HANDLE watek1 = NULL, watek2 = NULL;
DWORD nr;/* można wykorzystać pojedynczą zmienną identyfikującą wątek */
printf("Proba utworzenia 2ch watkow...n");
watek1 = CreateThread(NULL,0,proc,NULL,0,&nr);
if (watek1 != NULL)/* tak można sprawdzić powodzenie operacji tworzenia wątku */
printf("Watek o numerze %u utworzonyn",nr);
watek2 = CreateThread(NULL,0,proc,NULL,0,&nr);
if (watek2 != NULL)
printf("Watek o numerze %u utworzonyn",nr);
for (licznik=0;licznik<=5;licznik++)
printf("WATEK GLOWNY: Licznik globalny programu: %dn",licznik);
Sleep(2000);/* sztuczne opóźnienie – niezalecane w wątku głównym! */
system("PAUSE");
CloseHandle(watek1);/* zwalniamy zasoby (uchwyty) po wątkach */
CloseHandle(watek2);
return 0;
}
 
ZADANIE: Zapoznaj się z powyższym przykładowym programem ex.c demonstrującym sposób tworzenia wątków. Przepisz/skopiuj powyższy program do środowiska programistycznego, skompiluj i uruchom. Zwróć uwagę na wyświetlane informacje o wartości poszczególnych zmiennych licznikowych – dają one wyobrażenie o przeplotach wykonania wątku głównego z wątkami potomnymi. Zauważ, że przy niektórych wykonaniach programu niektóre wątki wykonują kilka swoich instrukcji pod rząd (bez oczekiwanego, „idealnego” wzajemnego przeplataniasię; da się to rozpoznać po wyświetlanych identyfikatorach wątków).
 
Program przykładowy ex1.c
/* Program wielowątkowy demonstrujący prostą synchronizację między wątkami
opartą na wspólnej zmiennej globalnej (stoper). Wada rozwiązania w rozważanym
przykładzie - wątek który jako pierwszy zakończy pętlę FOR, ustawi zmienną
"stoper" i w ten sposób może uniemożliwić wykonanie do końca drugiego wątku
poprzez odblokowanie wątku głównego (wątek główny może się w ten sposób
zakończyć terminując swoich potomków).
Dodatkowo przykład demonstruje sposób przekazywania parametrów do funkcji
wątków - tutaj pewnej struktury (dwa pola: ciąg znaków oraz liczba).
*/
#include <windows.h>
#include <stdio.h>
/* Definicja typu strukturalnego i typu wskaźnikowego na strukturę,
wykorzystamy te definicje w funkcji wątków do przekazania parametrów
*/
typedef struct
{
char napis[10];
int liczba;
} TPARAM, *PTPARAM;
/* Zmienne globalne, w tym "stoper" służący do synchronizacji */
int licznik=0, stoper=0;
/* Funkcja wątku - kilka wątków może wykonywać tą samą lub różne funkcje */
DWORD WINAPI proc(LPVOID arg)
{
static int licznik1=0;/* tą zmienną wątki będą współdzielić */
int i,licznik2=0;/* a to są już prywatne dane wątków */
PTPARAM parametr;/* zmienna na parametr przekazany do wątku - u nas struktura */
for (i=0;i<10;i++)
{
++licznik1; ++licznik2;
printf("WATEK %u: licznik statyczny watku: %dn",GetCurrentThreadId(),licznik1);
printf("WATEK %u: licznik prywatny watku: %dn",GetCurrentThreadId(),licznik2);
}
/* pierwszy z wątków który zakończy pętlę ustawi stoper */
parametr = (PTPARAM) arg;/* przypisanie argumentu do zmiennej prywatnej */
/* Wypisanie pola napisowego oraz liczby - liczbę po wszystkim inkrementujemy */
printf("Parametr:%s, %un",parametr->napis,parametr->liczba++);
stoper=1;/* ustawienie zmiennej synchronizującej */
}
int main(void)
{
HANDLE watek1, watek2;/* uchwyty dla wątków */
DWORD nr;/* zmienna na numer ID wątku */
TPARAM zm;/* tą zmienną przekażemy do funkcji wątków */
/* Wypełnienie pól struktury */
printf("Wpisz jakis tekst (do 10 znakow):");
scanf("%s",zm.napis);
printf("A teraz liczbe calkowita dodatnia:");
scanf("%u",&zm.liczba);
/* Próba utworzenia wątków - można nie zapamiętywać uchwytów, jeśli nie będą
nam potrzebne (nie wywołamy żadnej funkcji wątkowej, która oczekuje podania uchwytu
do identyfikacji wątku
*/
watek1 = CreateThread(NULL,0,proc,&zm,0,&nr);
if (watek1 != NULL)
printf("Watek o numerze %u utworzonyn",nr);
watek2 = CreateThread(NULL,0,proc,&zm,0,&nr);
if (watek2 != NULL)
printf("Watek o numerze %u utworzonyn",nr);
/* Program główny też coś robi */
for (licznik=0;licznik<=5;licznik++)
printf("PROGRAM: Licznik globalny programu: %dn",licznik);
/* Program główny zatrzymuje się - aktywne oczekiwanie, czyli prosta pętla */
while (stoper != 1)
printf("Program czeka...n");
CloseHandle(watek1);
CloseHandle(watek2);
system("PAUSE");
return 0;
}
 
ZADANIE: Zapoznaj się z przykładowym programem ex1.c demonstrującym sposób przekazywania parametrów do funkcji wątków oraz zasadę synchronizacji poprzez zmienne globalne. Napisz program, który pobiera od użytkownika 20 liczb (zapisując je w zmiennej tablicowej), tworzy dwa wątki i przesyła do nich (jako parametry ich funkcji) po połowie tablicy. Wątki sortują otrzymane połówki tablicy, informują program/wątek główny o zakończeniu swojej pracy, zaś ten wyświetla posortowane połówki, następnie scala je w jedną posortowaną całość i ją wyświetla. Zastanów się jak poinformować program/wątek główny o zakończeniu pracy przez wszystkie wątki w sytuacji, kiedy wykonują tą samą funkcję.
Wskazówka - jedno z możliwych rozwiązań:
Zmienna synchronizacyjna może być wyzerowaną tablicą o liczbie pozycji równej liczbie utworzonych wątków. Każdy wątek po zakończeniu pracy ustawia (0 -> 1) jedną pozycję tablicy (zrób użytek ze zmiennych prywatnych i wspólnych w funkcji wątku do indeksacji tablicy). Aktywne oczekiwanie programu/wątku głównego kończy się w momencie ustawienia wszystkich pozycji.
PRZYDATNE ŹRÓDŁA:
Opis wątków i procesów w Windows (interesują nas tylko wątki - threads):
http://msdn2.microsoft.com/en-us/library/ms684841.aspx
Pisanie programów wielowątkowych w Windows:
http://msdn2.microsoft.com/en-us/library/y6h8hye8(VS.71).aspx
Synchronizacja wielobieżności w programach Windows (interesują nas sekcje krytyczne, semafory i zdarzenia – critical section, semaphores, events)
http://msdn2.microsoft.com/en-us/library/ms686353.aspx
Opisy biblioteczne funkcji i struktur używanych w programowaniu wielowątkowym:
http://msdn2.microsoft.com/en-us/library/aa908719.aspx



_________________________________________________________________________




  #include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
// Pozmieniać komentarze !!!! i inne rzeczy !!!!


// deklaracje tablic
int tab0[20];
int tab1[10];
int tab2[10];
int stoper1=-1;
int stoper2=-1;
int stoper3=-1;

// deklaracje funkcji
void zerotabkoptab();       // zerowanie wszystkich tablic, utworzenie tab0 z
                            // liczb losowych przekopiowanie tab0 do tab1 i tab2
//void wyswietltab0();        // wyswietlenie tablicy 0
//void wyswietltab12();       // wyswietlanie tablic 1 i 2
//void sortowanie1(int tab1);         // sortowanie tab1
//void sortowanie2(int tab2);         // sortowanie tab2
//void sortowanie3();                 // sortowanie merge
// =============================================================================
void zerotabkoptab()
{
// =====================================================
/* zerowanie tablicy tab 0 i wypelnienie liczbami losowymi z zakresu <1;100>*/
int i;
      for(i=0 ; i<20 ; i++)
      {
            tab0[i] = 0;
            tab0[i] = 1+rand()%100;
    }
/* zerowanie tablic: tab1 i tab2*/
    for(i=0 ; i<10 ; i++)
    {
        tab1[i] = 0;
        tab2[i] = 0;
    }
// =====================================================
/* przekopiowanie tablicy "tab0" do tablic: "tab1" i "tab2"*/
    for(i=0 ; i<20 ; i++)
      {
        if(i<10)
          {
          tab1[i] = tab0[i];
          }
          if(i>9)
          {
            tab2[i-10] = tab0[i];
          }
      }
}
// =============================================================================
void wyswietltab0()
{
int z=0;
int y=0;
/* wyświetlenie elementów tablicy tab0*/
     printf("Numer tablicyn");
     for(z=0 ; z<20 ; z++)
     {
             
             printf("%2i ", z+1);
     }
     printf("n");
     printf("Kolejne elementyn");
     for(y=0 ; y<20 ; y++)
       {
             printf("%2i ", tab0[y]);
     }
       printf("n");
}
void wyswietltab12()
{
int k,l;
/* wyświetlenie tablic: tab1 i tab2*/
    printf("n");
    printf("Tab 1n");
    for(k=0 ; k<10 ; k++)
    {
        printf("%i ", tab1[k]);
    }

    printf("n");
  printf("Tab 1n");
    for(l=0 ; l<10 ; l++)
    {
        printf("%i ", tab2[l]);
    }
printf("n");
}
// =============================================================================
DWORD WINAPI sortowanie1()                              // LPVOID a[] LPVOID tab1[]
{
int i,j,w;
int ijtmp=0;
int tmp = 0;
/* sortowanie tablicy: "tab1"*/
    for(w=0; w<10 ; w++)
    for(i=0; i<10 ; i++)
    
    {
        for(j=0; j<10 ; j++)
        {
            if(tab1[i]<tab1[j])
            {
                ijtmp = tab1[i];
                tab1[i]= tab1[j];
                tab1[j] = ijtmp;
            }
        }
    }
    stoper1 = 1;
}
DWORD WINAPI sortowanie2()                              // LPVOID a[] LPVOID tab2[]
//void sortowanie2()
{
int i,j,w;
int ijtmp=0;
int tmp = 0;
/* sortowanie tablicy: "tab2"*/
    for(w=0; w<10 ; w++)
    for(i=0; i<10 ; i++)
    {
        for(j=0; j<10 ; j++)
        {
            if(tab2[i]<tab2[j])
            {
                ijtmp = tab2[i];
                tab2[i]= tab2[j];
                tab2[j] = ijtmp;
            }
        }
    }
    stoper2 = 1;
}
// =====================================================
DWORD WINAPI sortowanie3()
{
int i,j;
i=0;
j=0;
int r=0;
int p;
 int roztab0=20;
 int roztab1=10;
 int roztab2=10;

printf("n");
printf("n");
/* przekopiowanie do tablicy tab0 elementów z tab1 i tab2
   po uprzednim posortowaniu pomocą sortowania merge*/
     do
     {
        if(tab1[i] >= tab2[j] && (j==(roztab2-1)))
        {
            tab0[r] = tab2[j];
            r++;
            j++;
            p = i;
            for(p ; p<roztab1 ; p++)
            {
                  tab0[r] = tab1[p];
                  r++;
                  i++;
            }
        }          
        if(tab1[i] <= tab2[j] && (i==(roztab1-1)))
        {
            tab0[r] = tab1[i];
            r++;
            i++;
            p = j;
            for(p ; p<roztab2 ; p++)
            {
                  tab0[r] = tab2[p];
                  r++;
                  j++;
            }
        }
        if(tab1[i] >= tab2[j])
        {
            tab0[r]=tab2[j];
            j++;
            r++;          
        }
        else
        {
            tab0[r]=tab1[i];
            i++;
            r++;
        }
     }
     while(roztab0 > r);
printf("n");
stoper3 = 1;
}
// -----------------------------------------------------------------------------
// ---------------------- POCZATEK PROGRAMU GŁÓWNEGO ---------------------------
// -----------------------------------------------------------------------------
int main(void)
{
srand(time(0));
HANDLE watek1;      // uchwyt dla wątku
HANDLE watek2;      // uchwyt dla wątku
HANDLE watek3;
DWORD nr;                   // zmienna na numer ID wątku
// =====================================================
    zerotabkoptab();
// =====================================================
    wyswietltab0();
    wyswietltab12();
    printf("n");
// =====================================================
watek1 = CreateThread(NULL,0,sortowanie1,NULL,0,&nr);
if (watek1 != NULL)
printf("Watek o numerze %u utworzonyn",nr);

watek2 = CreateThread(NULL,0,sortowanie2,NULL,0,&nr);
if (watek2 != NULL)
printf("Watek o numerze %u utworzonyn",nr);

// =====================================================
   while ((stoper1!=1)||(stoper2!=1))
   printf(". ");

 
    printf("n");
    
    if((stoper1 == 1) && (stoper2 == 1))
    wyswietltab12();

    printf("n");
// ====================================================
watek3 = CreateThread(NULL,0,sortowanie3,NULL,0,&nr);
if (watek3 != NULL)
printf("Watek o numerze %u utworzonyn",nr);
printf("n");
// =====================================================
while (stoper3!=1)
{
      printf(". ");
}
// =====================================================
printf("n");           
if(stoper3 == 1)
{
wyswietltab0();
}
// ======================================================
   CloseHandle(watek1);
   CloseHandle(watek2);
   CloseHandle(watek3);

   getchar();
   return 0;
}
// -----------------------------------------------------------------------------
// --------------------- KONIEC PROGRAMU GłÓWNEGO ------------------------------
// -----------------------------------------------------------------------------