Hlavní navigace

Programování pod Linuxem pro všechny (15)

Jakub Matys 7. 5. 2004

Dnes si povíme něco o speciální datové oblasti vláken, která umožňuje vláknům mít vlastní kopii dat.

Data specifická pro vlákna

Všechna vlákna uvnitř jednoho programu, na rozdíl od procesů, sdílejí tentýž adresový prostor. Pokud tedy jedno vlákno modifikuje nějakou část paměti (např. globální proměnnou), je tato změna viditelná v ostatních vláknech. To umožňuje více vláknům operovat se stejnými daty bez vzájemné komunikace (tu proberu v některém z příštích dílů).

Každé vlákno má však svůj vlastní zásobník (call stack). Tím je zajištěno, že každé vlákno může realizovat svůj vlastní kód a volat různé další funkce podle svých potřeb. V programu s jediným vláknem platí, že každé volání podprogramu v každém vlákně má svoji vlastní množinu lokálních proměnných, jež jsou uloženy v zásobníku tohoto vlákna.

Někdy je požadováno, aby jistá proměnná byla duplicitně vytvořena tak, aby každé vlákno mělo svou vlastní kopii. Linux podporuje tento požadavek tím, že poskytuje každému vláknu datovou oblast specifickou pro vlákna. Proměnné uložené do této oblasti jsou duplikovány pro každé vlákno, a proto každé vlákno může takovou proměnnou modifikovat, aniž by se změnila její hodnota pro jiná vlákna. Protože jinak vlákna sdílejí tentýž paměťový prostor, nemohou být data ve speciální datové oblasti zpřístupňována normálním způsobem. Pro tento účel se používají speciální funkce, jež umožňují nastavovat a získávat data z této oblasti.

Speciální data pro vlákna musejí být typu void * a můžete jich vytvořit jakékoliv množství. Každá položka dat se zpřístupňuje pomocí klíče. K vytvoření klíče a tedy k vytvoření nové datové položky je určena funkce pthread_key_cre­ate(). Prvním argumentem je ukazatel na proměnnou pthread_key_t. Klíčová hodnota může být použita každým vláknem ke zpřístupnění své vlastní kopie datové položky. Druhý argument funkce pthread_key_cre­ate() je tzv. čistící funkce (cleanup function). Pokud pomocí druhého argumentu předáte ukazatel na funkci, provede se její kód a bude jí předána hodnota specifická pro tento klíč. Funkce se provede vždy, i když se vlákno zruší v nějakém náhodném bodě své realizace. Pokud čistící funkci nechcete používat, zadejte místo druhého argumentu NULL.

Po vytvoření klíče může každé vlákno nastavit svoji vlastní hodnotu odpovídající tomuto klíči prostřednictvím funkcepthread_set­specific(). Prvním argumentem této funkce je samotný klíč a druhým argumentem je speciální hodnota typu void *, která se má uložit. Ke zpětnému získání dat lze použít funkci pthread_getspe­cific(), které se jako argument předává klíč.

Předpokládejme například, že vaše aplikace má řešený problém rozdělen mezi několik vláken. Abychom si zachovali přehled, nechť každé vlákno má svůj vlastní protokol (log file), ve kterém je zaznamenáno, jak realizace vlákna pokračuje. Speciální datová oblast je ideálním místem, kam uložit ukazatel na protokol, a to pro každé vlákno.

Funkce main() v tomto příkladu vytváří klíč k uložení ukazatele na protokol a ukládá jej v proměnné thread_log_key. Protože se jedná o globální proměnnou, je sdílena všemi vlákny. Když každé vlákno začíná svoji realizaci prostřednictvím funkce vlákna, otevře protokol a ukládá ukazatel na soubor pod tímto klíčem. Později může každé z vytvořených vláken volat funkci write_to_thre­ad_log, pomocí níž zapisuje do protokolu zprávy pro konkrétní vlákno. Tato funkce zjistí ukazatel na soubor pro konkrétní vlákno ze speciální datové oblasti a zapíše zprávu.

#include <malloc.h>
#include <pthread.h>
#include <stdio.h>

/* Klic pouzivany k asociaci dat */
static pthread_key_t thread_log_key;

/* Zapise MESSAGE do logovaciho souboru pro vlakno */

void write_to_thread_log (const char *message)
{
  FILE *thread_log = (FILE *) pthread_getspecific(thread_log_key);
  fprintf(thread_log, "%s\n", message);
}

/* Zavre logovaci soubor vlakna */

void close_thread_log (void *thread_log)
{
  fclose((FILE *) thread_log);
}

void *thread_function (void *args)
{
  char thread_log_filename[20];
  FILE *thread_log;

  /* Generuje jmeno pro logovaci soubor vlakna */
  sprintf (thread_log_filename, "thread%d.log", (int) pthread_self());
  /* Otevre logovaci soubor */
  thread_log = fopen(thread_log_filename, "w");
  /* Ulozi ukazatel na soubor do specialni
     datove oblasti vlakna zpristupnovane
     pomoci thread_log_key */
  pthread_setspecific(thread_log_key, thread_log);

  write_to_thread_log("Thread starting.");

  /* Zde vlakno pokracuje ve sve realizaci a
     zapisuje informace do logovaciho souboru */

  return(NULL);
}

int main()
{
  int i;
  pthread_t threads[5];

  /* Vytvori klic pro asociaci ukazatelu na
     logovaci soubory v specialni datove
     oblasti. Jako cistici funkce se pouzije
     close_thread_log, ktera uzavira logovaci
     soubory. */
  pthread_key_create (&thread_log_key, close_thread_log);
  /* Vytvori vlakna */
  for (i = 0; i < 5; ++i)
    pthread_create (&(threads[i]), NULL, thread_function, NULL);
  /* Ceka na ukonceni vsech vlaken */
  for (i = 0; i < 5; ++i)
    pthread_join (threads[i], NULL);
  return(0);
} 

Všimněte si, že funkce thread_function() nepotřebuje uzavírat protokol. Je tomu tak proto, že při vytvoření klíče pro protokol byla funkce close_thread_log() specifikována jako čistící funkce pro tento klíč. Kdykoliv se vlákno ukončí, Linux zavolá funkci close_thread_log(), a ta se postará o uzavření protokolů.

To je pro dnešek vše, příště se budeme zabývat podrobněji čistícími funkcemi a povíme si něco o synchronizaci vláken.

Našli jste v článku chybu?

24. 9. 2004 16:08

JeRRy (neregistrovaný)

Jakto ze se to musi resit takto ?
Prece funkce thread_function je handler tohoto vlakna
a kazdy vlakno bude mit svuj vlastni zasobnik
NE?
takze lokalni promene
char thread_log_filename[20];
FILE *thread_log;
budou pro kazde vlakno rozdilne a nebudou si ovlivnovat navzajem ne ?
prece neni ANI GLOBALNI ANI STATIC viz manualove stranky (A NEBO JSEM TO SPATNE POCHOPIL).









12. 5. 2004 12:43

uživatel si přál zůstat v anonymitě

Existuje nejakej jednoduchej zpusob jak v Linuxu zkontrolovat zda vlakno skoncilo a pokud ne tak necekat, ale hned pokracovat (asi jako waitpid s WNOHANG u procesu) ?

Lupa.cz: Seznam mění vedení. Pavel Zima v čele končí

Seznam mění vedení. Pavel Zima v čele končí

Podnikatel.cz: Přehledná titulka, průvodci, responzivita

Přehledná titulka, průvodci, responzivita

Vitalia.cz: Z tohoto konopí dělají léčivé masti

Z tohoto konopí dělají léčivé masti

Měšec.cz: Air Bank zruší TOP3 garanci a zdražuje kurzy

Air Bank zruší TOP3 garanci a zdražuje kurzy

Lupa.cz: Proč firmy málo chrání data? Chovají se logicky

Proč firmy málo chrání data? Chovají se logicky

Vitalia.cz: Dáte si jahody s plísní?

Dáte si jahody s plísní?

Podnikatel.cz: Babiš: E-shopy z EET možná vyjmeme

Babiš: E-shopy z EET možná vyjmeme

Vitalia.cz: Jsou čajové sáčky toxické?

Jsou čajové sáčky toxické?

Lupa.cz: Babiš: E-shopů se EET možná nebude týkat

Babiš: E-shopů se EET možná nebude týkat

DigiZone.cz: Sony KD-55XD8005 s Android 6.0

Sony KD-55XD8005 s Android 6.0

Lupa.cz: Propustili je z Avastu, už po nich sahá ESET

Propustili je z Avastu, už po nich sahá ESET

Vitalia.cz: Mondelez stahuje rizikovou čokoládu Milka

Mondelez stahuje rizikovou čokoládu Milka

Měšec.cz: Jak vymáhat výživné zadarmo?

Jak vymáhat výživné zadarmo?

Měšec.cz: Jak levně odeslat balík přímo z domu?

Jak levně odeslat balík přímo z domu?

Podnikatel.cz: K EET. Štamgast už peníze na stole nenechá

K EET. Štamgast už peníze na stole nenechá

DigiZone.cz: ČT má dalšího zástupce v EBU

ČT má dalšího zástupce v EBU

Podnikatel.cz: Chaos u EET pokračuje. Jsou tu další návrhy

Chaos u EET pokračuje. Jsou tu další návrhy

120na80.cz: Jak oddálit Alzheimera?

Jak oddálit Alzheimera?

Root.cz: Vypadl Google a rozbilo se toho hodně

Vypadl Google a rozbilo se toho hodně

Root.cz: Certifikáty zadarmo jsou horší než za peníze?

Certifikáty zadarmo jsou horší než za peníze?