Hlavní navigace

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

Jakub Matys

Dnes si povíme, jak řídit činnost vláken pomocí mutexu.

Mutex

Řešení problému souběhu při zpracování úloh, jak jsem jej uvedl v minulém dílu, spočívá v tom, že se dovolí pouze jednomu vláknu v daný okamžik zpřístupnit frontu úloh. Jakmile začne vlákno prohledávat frontu, žádné jiné vlákno k ní nesmí mít přístup, dokud se naše vlákno nerozhodne, zda úlohu zpracovat, a když ano, dokud ji nezpracuje.

Implentace takového řešení vyžaduje podporu operačního systému. Proto Linux poskytuje tzv. mutexy. Mutex je speciální typ zámku, který v daný okamžik může být uzavřen pouze jediným vláknem. Pokud jedno vlákno uzamkne mutex a pak se druhé vlákno pokusí také mutex uzamknout, zablokuje se. Teprve až první vlákno odemkne mutex, druhé se odblokuje a může dále pokračovat v činnosti. Systémem je garantováno, že se problém souběhu nemůže vyskytnout mezi vlákny pokoušejícími se uzamknout mutex. Vždy pouze jedno vlákno dostane možnost mutex uzamknout a ostatní budou zablokována.

K vytvoření mutexu je určena proměnná typu pthread_mutex_t. Po deklaraci takové proměnné musíte předat její ukazatel funkcipthread_mu­tex_init(). Druhým argumentem funkce pthread_mutex_i­nit() je ukazatel na atributy mutexu, což je objekt specifikující vlastnosti mutexu. Pokud je ukazatel nulový, předpokládají se implicitní atributy. Proměnná mutexu by měla být inicializována pouze jednou.

Příklad vytvoření mutexu:

pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL);

Jiný způsob, jak vytvořit mutex s implicitními atributy, spočívá v inicializaci speciální hodnotou PTHREAD_MUTEX_I­NITIALIZER. Pak není nutné žádné dodatečné volání funkce pthread_mutex_i­nit(). Tento způsob je vhodný pro globální proměnné:

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

Vlákno se může pokusit uzamknout mutex voláním funkce pthread_mutex_loc­k(). Pokud byl mutex odemknut, uzamkne se a funkce se vrátí okamžitě. Pokud ovšem byl mutex uzamknut jiným vláknem, funkce zablokuje další realizaci vlákna a vrátí se, až bude mutex odemknut. Může být také zablokováno více vláken najednou, ale nelze určit, které se po odemknutí mutexu odblokuje. Toto vlákno může mutex opět uzamknout. Ostatní vlákna zůstanou zablokována.

Voláním funkce pthread_mutex_un­lock() se mutex odemkne. Tato funkce by měla být vždy volána z téhož vlákna, které mutex uzamklo.

Následující příklad ukazuje využití mutexu s frontou úloh. Jedná se o přepis zdrojového kódu z minulého dílu:

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

struct job{
  struct job *next;
};

struct job *job_queue;

/* Vytvoreni mutexu s implicitnimi atributy */
pthread_mutex_t job_queue_mutex = PTHREAD_MUTEX_INITIALIZER;

void *thread_function(void *arg)
{
  while(1) {
    struct job *next_job;
    /* Uzamknuti mutexu pred zpracovanim fronty */
    pthread_mutex_lock(&job_queue_mutex);
    if (job_queue == NULL)
      next_job = NULL;
    else {
      next_job = job_queue;
      job_queue = job_queue->next;
    }
    /* Odemceni mutexu */
    pthread_mutex_unlock(&job_queue_mutex);

    if (next_job == NULL)
      break;

    process_job(next_job);
    free(next_job);
  }
  return(NULL);
} 

Veškeré přístupy k proměnné job_queue (sdílenému ukazateli) probíhají mezi voláním funkcí pthread_mutex_loc­k() a pthread_mutex_un­lock(). Objekt job, uložený v proměnné next_job, je zpřístupňován vně této oblasti pouze tehdy, až je tento objekt odstraněn z fronty. Proto je nepřístupný ostatním vláknům.

Všimněte si, že pokud je fronta prázdná (hodnota proměnné job_queue je nulová), neukončuje se cyklus okamžitě, protože by mohl mutex zůstat permanentně uzamknut a k frontě by už nezískalo přístup žádné jiné vlákno.

To by bylo pro dnešek vše, příště se podíváme na problém zvaný uváznutí mutexu a možná, pokud vyzbyde místo, si povíme něco o semaforech pro vlákna.

Našli jste v článku chybu?