Warning
This page is located in archive.

Vlákna a synchronizace

Na tomto cvičení byste si měli vyzkoušet jak vytvořit vlákno v jazyce C s využitím knihovny pthread a jak vlákna synchronizovat tak, aby nedošlo k poškození dat se kterými se pracuje z více vláken.

Domácí příprava

Pro toto cvičení budete potřebovat znalosti o tom

  • co jsou vlákna, mutexy a semafory,
  • jak tyto prostředky vytvoříte v jazyce C s využitím knihovny pthread,
  • jak se vlákna vytvářejí a ukončují,
  • jaké problémy mohou nastávat při paralelním běhů vláken a
  • jak psát programy tak, aby tyto problémy nenastaly.

Potřebná teorie byla vyložena na přednášce, včetně ukázek použití funkcí knihovny pthread. Před cvičením je doporučeno podívat se na manuálové stránky potřebných funkcí, zejména

  • pthread_create, pthread_join,
  • pthread_mutex_init, pthread_mutex_destroy, pthread_mutex_lock, pthread_mutex_unlock
  • sem_init, sem_destroy, sem_wait, sem_post.

Zadání úlohy

Implementujte vícevláknový program prod-cons splňující následující požadavky:

  • V hlavním vlákně (funkce main()) se vytvoří jedno vlákno, kterému budeme říkat producent a dále N vláken konzument.
  • Hodnota N bude zadávána jako parametr při spouštění programu (argv[1]). Pokud žádný parametr nebude zadán, bude N rovno 1.
  • Předpokládejte, že N bude v rozmezí 1 až počet CPU v systému (sysconf(_SC_NPROCESSORS_ONLN)). Pokud bude zadána jiná hodnota, program skončí s návratovým kódem 1.
  • Producent bude splňovat následující:
    • Bude číst ze standardního vstupu “příkazy” ve formě dvojic “<X> <slovo>”, kde <X> je celé nezáporné číslo a <slovo> je libovolná neprázdná sekvence znaků (kromě whitespace). X od slova může, ale nemusí, být odděleno bílými znaky (whitespace), jednotlivé příkazy jsou od sebe vždy odděleny jedním či více bílými znaky (mezera, konec řádku). Délka slova je omezená pouze velikostí dostupné paměti.
      • K načítání příkazů doporučujeme použít následující kód:
        int ret, x;
        char *text;
        while ((ret = scanf("%d %ms", &x, &text)) == 2) {
            ...
        }
        Direktiva %ms (malloc string) způsobí, že scanf dynamicky alokuje takové množství paměti, které je potřeba pro uložení načítaného slova. Nezapomeňte potom tuto paměť uvolnit funkcí free().
    • Pokud bude zadán neplatný příkaz (tj. neodpovídající předchozímu bodu), program skončí s návratovým kódem 1.
    • Pro každý přečtený příkaz dynamicky alokuje (malloc(), new, …) datovou strukturu, uloží do ní X a slovo a zařadí jí na konec spojového seznamu.
  • Každý konzument bude splňovat následující:
    • Bude ze začátku spojového seznamu vybírat položky vkládané producentem.
    • Pokud v seznamu žádná položka není, bude čekat, až tam producent něco přidá (bez spotřeby výpočetního času, žádný polling).
    • Pokud producent přidá P položek, vzbudí se maximálně P konzumentů, ostatní budou dále čekat.
    • Pro každou vyzvednutou položku konzument vypíše na standardní výstup řetězec “Thread n: slovo slovo slovo…”, kde n je číslo konzumenta (pořadí vytvoření konzumenta v rozsahu 1–N) a slovo se opakuje X-krát (informace od producenta). Tento řetězec bude celý na jedné řádce ukončené \n.
  • Pouze producent bude číst ze standardního vstupu.
  • Pouze konzumenti budou zapisovat na standardní výstup.
  • Standardní chybový výstup můžete použít k ladícím výpisům.
  • Uzavření standardního vstupu je požadavkem na ukončení programu. Pokud není řečeno jinak, návratový kód bude 0.
  • Všechny platné “příkazy” zaslané na standardní vstup budou mít odpovídající řádku na standardním výstupu (nic se neztratí).
  • Žádné čekání ve vašem programu by nemělo být implementováno formou pollingu (periodická kontrola, že se něco stalo).
  • Program před ukončením dealokuje všechnu dynamicky alokovanou paměť, kterou alokoval.

Do odevzdávacího systému nahrajte svůj zdrojový kód a Makefile, který vygeneruje program prod-cons ve stejném adresáři jako Makefile. Program překládejte s přepínači -Wall a -g. Překladač nesmí generovat žádná varování.

Pokud vám BRUTE vrací podezřelé chybové hlášky, zkuste váš program navíc zkompilovat s přepínači -fsanitize=address -fno-omit-frame-pointer. První přepínač způsobí, že se do vygenerovaného kódu přidají kontroly na mnoho typických chyb. Výsledný program pak ale běží mnohem pomaleji a potřebuje mnohem více paměti. Druhý přepínač způsobí, že “stack trace” ve vypisovaných chybových hláškách bude srozumitelnější.

Před uploadem do BRUTE tyto přepínače zase odstraňte, protože program pak díky pomalosti a paměťovým nárokům neprojde některými testy a navíc vygenerované kontroly nejsou kompatibilní s kontrolami, které provádí BRUTE.

Domácí příprava na další cvičení

Nastudujte si použití podmínkový proměnných a k tomu příslušné funkce v knihovně pthread:

  • pthread_cond_init
  • pthread_cond_destroy
  • pthread_cond_signal
  • pthread_cond_broadcast
  • pthread_cond_wait
courses/b4b35osy/cviceni/cviceni5_vlakna.txt · Last modified: 2018/11/15 22:51 by sojkam1