Hlavní navigace

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

30. 4. 2004
Doba čtení: 4 minuty

Sdílet

Dnes se podíváme na asynchronně a synchronně zrušitelná vlákna a nezrušitelná vlákna.

Vlákno často obsahuje kód, který se buď musí realizovat celý, nebo vůbec. Může například alokovat nějaké zdroje, použít je a poté je dealokovat. Když bude takovéto vlákno přerušeno uprostřed své činnosti, mohou použité zdroje zůstat alokovány. Abychom mohli takové situaci předcházet, je možné rozhodovat, zda má být vlákno zrušeno a kdy.

Vlákno může být v jednom ze tří možných stavů s ohledem na možnost jeho zrušení:

  • Vlákno může být asynchronně zrušitelné – může být zrušeno v kterémkoliv svém stavu.
  • Vlákno může být synchronně zrušitelné – může být zrušeno, ale ne ve všech svých stavech. Požadavky na zrušení se ukládají do fronty a vlákno je zrušeno, až provádění kódu dospěje do místa, které to umožňuje.
  • Vlákno může být nezrušitelné – pokusy o zrušení vlákna se ignorují.

V okamžiku vzniku je každé vlákno synchronně zrušitelné.

Synchronní a asynchronní vlákna

Asynchronně zrušitelné vlákno může být zrušeno v kterémkoliv okamžiku své realizace. Synchronně zrušitelné vlákno může být zrušeno jen v určitých okamžicích. Místa, ve kterých může být vlákno zrušeno, se nazývají body zrušení (cancellation points). Takové vlákno bude ukládat požadavky na přerušení do fronty a zruší se až po dosažení bodu zrušení.

Vlákno můžete učinit asyncronně zrušitelné pomocí funkce pthread_setcan­celtype(). Tato funkce má vliv pouze na vlákno, které ji volá. Prvním argumentem funkce musí býtPTHREAD_CAN­CEL_ASYNCHRONO­US (pak bude vlákno asynchonně zrušitelné) nebo PTHREAD_CANCEL_DE­FERRED (vlákno pak bude syncronně zrušitelné). Druhý argument, pokud není nulový, je ukazatel na proměnnou, která obdrží předcházející typ zrušení vlákna. Následující příkaz učiní vlákno asynchronně zrušitelné.

pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); 

Co tvoří bod zrušení a jak se definuje? Nejjednoduší způsob, jak vytvořit bod zrušení, spočívá ve volání funkce pthread_testcan­cel(). Tato funkce nedělá nic jiného, než že zpracovává požadavky na zrušení čekající ve frontě. Funkci pthread_testcan­cel() byste měli vkládat periodicky do kódu funkce obsahující zdlouhavé výpočty tam, kde může být vlákno zrušeno bez dealokace systémových zdrojů.

Existují také funkce, které samy o sobě představují body zrušení. Jejich seznam najdete v manuálových stránkách pthread_cancel.

Nezrušitelné kritické sekce

Vlákno může znemožit své vlastní zrušení pomocí funkce pthread_setcan­celstate(). Podobně jako v případě pthread_setcan­celtype() má i tato funkce vliv pouze na volající vlákno. Prvním argumentem je PTHREAD_CANCEL_DI­SABLE (znemožnění zrušení vlákna) nebo PTHREAD_CANCEL_E­NABLE (opětovné umožnění zrušení vlákna). Následující volání například znemožní zrušení ve volajícím vlákně:

pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); 

Funkce pthread_setcan­celstate() vám umožní implentovat tzv. kritickou sekci. Kritická sekce je část kódu, která musí být buď realizována celá, nebo vůbec ne. Jinými slovy jakmile začalo vlákno realizovat kritickou sekci, musí ji dokončit, aniž by mohlo být zrušeno.

Předpokládejme například, že píšete kód pro bankovní program, který transferuje peníze z jednoho účtu na druhý. To znamená, že musíte přidat nějakou hodnotu do bilance na jednom účtu a odečíst tutéž hodnotu z druhého účtu. Kdyby mohlo být vlákno zrušeno v nějakém kritickém místě mezi těmito dvěma operacemi, pak by se asi zhoršovalo renomé banky. Abyste takové situaci zabránili, umístěte zmíněný kód do kritické sekce.

Popsaný transfer peněz můžete implentovat funkcí process_transac­tion(), jejíž ukázka je níže. Tato funkce znemožní zrušení vlákna na začátku kritické sekce, tedy před tím, než se modifikuje bilance kteréhokoliv účtu.

CS24_early

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

/* pole zustatku na uctech, indexovane cisly uctu */
float *account_balances;

/* Prevede EURO z uctu FROM_ACCT na ucet TO_ACCT.
   Vraci 0, pokud byla transakce provedena uspesne,
   nebo 1, pokud je zustatek na uctu FROM_ACCT
   prilis maly
 */

int process_transaction (int from_acct, int to_acct, float euro)
{
  int old_cancel_state;
  /* Kontrola zustatku na uctu FROM_ACCT */
  if (account_balances[from_acct] < euro )
     return(1);

  /* zacatek kritikce sekce */
  pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, &old_cancel_state);
  /* prevod penez */
  account_balances[to_acct] += euro;
  account_balances[from_acct] -= euro;
  /* konec kriticke sekce */
  pthread_setcancelstate (old_cancel_state, NULL);

  return(0);
} 

Všiměte si, že je nutné obnovit starý stav zrušení na konci kritické sekce místo bezpodmínečného nastavení na PTHREAD_CANCEL_E­NABLE. Tím je umožněno, aby se funkce process_transac­tion() volala bezpečně uvnitř ktrékoliv jiné kritické sekce – v tomto případě naše funkce opustí stav zrušení stejným způsobem, jakým jej nabyla.

To by bylo pro dnešek vše, příště se podíváme na data specifická pro vlákna.

Byl pro vás článek přínosný?

Autor článku