Teorie:

Bez synchronizace:

Race conditions (Časově závislé chyby):

Situace, kdy více vláken používá (R/W access) společné sdílené prostředky během deterministického algoritmu a jeho výsledek je závislý na rychlosti vláken

Při použití synchronizace:

Deadlock (Uváznutí):

Situace, kdy několik vláken čeká na událost, kterou může vyvolat pouze jedno z čekajících vláken

Livelock:

Situace, kdy několik vláken vykonává neužitečný výpočet (mění svůj stav), ale nemohou dokončit výpočet

Starvation (Hladovění):

Situace, kdy je vlákno ve stavu "Ready" předbíháno a nedostane se po "dlouhou" dobu k prostředkům.

Programy:

Fork

Kolikrát se zobrazí řetězec test?

if(fork()
    || fork()
        ...
    || fork())
    printf("test\n");

zde stačí spočítat počet "fork()" a daný počet bude odpovědí.


Kolikrát se zobrazí řetězec test?

if (fork() 
    && fork() 
        ...
    && fork())
    printf("test\n");

Řetězec test se vytiskne pouze 1.


Kolik procesů vznikne důsledkem spuštění následujícího kódu?

for( int i = 0; i < X; i++ ){
  if( fork() == fork() )
    break;
}


Zapsáno jinak:


Procesy a vlákna

int g_X = (g_X);
mutex g_M[4];

void foo (int pos)
{
  lock_guard<mutex> l0(g_M [pos%4]);
  lock (g_M[(pos+1)%4], g_M[(pos+2)%4]);
  g_X += pos;
}
...
thread t1 (foo, 17);
thread t2 (foo, 18);
thread t3 (foo, 7);
thread t4 (foo, 8);

t1.join()
t2.join()
t3.join()

Platí:

Možné uváznutí (deadlock)
Možné aktivní čekání (busy waiting)
Možné vyhladovění (livelock)
Pokud doběhne, tak může být


int g_X = 40;
mutex g_M[4];

void foo ( int pos ) {
  lock_guard<mutex> l0 ( g_M[pos % 4], g_M[(pos+1) % 4], g_M[(pos+2) % 4] );
  g_X += pos;
}
 
int main(int argc, const char *argv[]) {
 
  thread t0 ( foo, 13 );
  thread t1 ( foo, 18 );
  thread t2 ( foo, 23 );
  thread t3 ( foo, 16 );
  ...
  cout << g_X << endl;
  ...
  t0.join();
  t1.join();
  t2.join();
  t3.join();
 
return 0;
}

Možné aktivní čekání (busy waiting)
Možné vyhladovění (livelock)


int g_X = 40;
mutex g_M[4];

void foo ( int pos ) {
  lock_guard<mutex> l0 ( g_M[ pos    % 4 ] );
  lock_guard<mutex> l1 ( g_M[ pos +1 % 4 ] );
  lock_guard<mutex> l2 ( g_M[ pos +2 % 4 ] );
  g_X += pos;
}

int main(int argc, const char *argv[]) {

  thread t0 ( foo, 13 );
  thread t1 ( foo, 22 );
  thread t2 ( foo, 13 );
  ...
  cout << g_X << endl;
  ...
  t0.join();
  t1.join();
  t2.join();

return 0;
}

output: 88.