Hlavní navigace

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

Jakub Matys

Dnes si povíme něco málo o předávání dat do vláken, spojování vláken a návratových kódech vláken.

Předávání dat do vlákna

Pomocí čtvrtého argumentu funkce pthread_create() lze předávat data do vlákna. Protože je typ argumentu void *, nemůžete předat do vlákna velké množství dat přímo prostřednictvím argumentu vlákna. Můžete však předat do vlákna datovou strukturu nebo pole dat. Nejčastěji se pro každé vlákno vytváří speciální datová struktura obsahující data, která vlákno očekává. Protože se předávání dat do vlákna vyřešilo takto univerzálně, můžete použít stejnou funkci pro několik vláken. Pak každé vlákno realizuje tentýž kód, ale s různými daty.

Program příkladu je podobný programům v předchozím dílu. Tento program vytvoří dvě nová vlákna. Tato vlákna budou tisknout znaky x a o, ale vytisknou jich pouze určitý počet a potom se ukončí. Obě vlákna používají stejnou funkci, avšak každé přijímá jiná data.

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

/* Struktura, ktera bude predavana vlaknum */
struct char_print_params
{
  /* Znak k tisknuti */
  char character;
  /* Pocet vytisknuti znaku */
  int count;
};

/* Tiskne znaky do stderr, data ziskava
   z argumentu PARAMETERS, ktery je ukazatel
   a strukturu char_print_params */

void *char_print(void *parameters)
{
  /* Nastavi ukazatel p na strukturu char_print_params */
  struct char_print_params *p = (struct char_print_params *) parameters;
  int i;

  for(i = 0; i < p->count; ++i)
    fputc(p->character, stderr);
  return NULL;
}

int main()
{
  pthread_t thread1_id;
  pthread_t thread2_id;
  struct char_print_params thread1_args;
  struct char_print_params thread2_args;

  /* Vytvori nove vlakno, ktere vytiskne
     30 000 krat pismeno x */
  thread1_args.character = 'x';
  thread1_args.count = 30000;
  pthread_create(&thread1_id, NULL, &char_print, &thread1_args);

  /* Vytvori nove vlakno, ktere vytiskne
     20 000 krat pismeno o */
  thread2_args.character = 'o';
  thread2_args.count = 20000;
  pthread_create(&thread2_id, NULL, &char_print, &thread2_args);

  return(0);
} 

Pozor!!! Výše uvedený program obsahuje závažnou chybu. Hlavní vlákno (to, ve kterém běží funkce main()) vytváří struktury pro předání parametrů (thread1_args a thread2_args) jako lokální proměnnou a pak ukazatel na tyto struktury předává jako parametr vláknům, která vytváří. Může se stát, že systém ukončí hlavní vlákno před ukončením podřízených vláken. Pokud se tak stane, paměť obsahující struktury pro paramenty se dealokuje, zatímco zbývající dvě vlákna ji budou používat.

Spojování vláken

Jedním možným řešením je, že se hlavní vlákno donutí počkat, až zbývající vlákna skončí. Proto potřebujeme podobnou funkci, jako je wait(), která čeká, až se ukončí podřízený proces. Touto funkcí je pthread_join() a má dva argumenty – identifikační číslo vlákna, na které má čekat, a ukazatel na proměnnou typu void *, které bude přiřazen návratový kód vlákna. Pokud vás nezajímá návratový kód, zadejte hodnutu NULL.

Následující příklad ukazuje opravenou funkci main().

int main()
{
  pthread_t thread1_id;
  pthread_t thread2_id;
  struct char_print_params thread1_args;
  struct char_print_params thread2_args;

  /* Vytvori nove vlakno, ktere vytiskne
     30 000 krat pismeno x */
  thread1_args.character = 'x';
  thread1_args.count = 30000;
  pthread_create(&thread1_id, NULL, &char_print, &thread1_args);

  /* Vytvori nove vlakno, ktere vytiskne
     20 000 krat prismeno 0 */
  thread2_args.character = 'o';
  thread2_args.count = 20000;
  pthread_create(&thread2_id, NULL, &char_print, &thread2_args);

  /* Ceka na ukonceni prvniho vlakna */
  pthread_join(thread1_id, NULL);
  /* Ceka na ukonceni druheho vlakna */
  pthread_join(thread2_id, NULL);

  return(0);
} 

Návratové kódy vláken

Pokud je druhý argument předávaný funkci pthread_join() nenulový, bude po ukončení vlákna obsahovat jeho návratový kód. Návratový kód je typu void *. Pokud chcete předat zpět jako návratový kód data nějakého datového typu (např. int, float, char), musíte tato data přetypovat na typ void *.

To by bylo pro dnešek vše, v příštím dílu si povíme něco o identifikačních číslech vláken, atributech vláken a rušení vláken.

Našli jste v článku chybu?