Vés al contingut

Semàfor (informàtica)

De la Viquipèdia, l'enciclopèdia lliure

Un semàfor és una variable especial protegida (o tipus abstracte de dades) que constitueix el mètode clàssic per a restringir o permetre l'accés als recursos compartits (per exemple, un recurs d'emmagatzematge del sistema o variables del codi font) en un en un entorn de concurrència, en què diversos processos s'executen concurrentment. Van ser inventats per Edsger Dijkstra i es van usar per primera vegada en el sistema operatiu THE.

Operacions

[modifica]

Els semàfors només poden ser manipulats usant les següents operacions (aquest és el codi amb espera activa):

Inicia (Semàfor s, Enter v)
{
 s = v;
}

En el que s'iniciarà la variable semàfor s a un valor enter v.

P (Semàfor s)
{
 if (s>0)
 s = s-1;
 else
 wait();
}

La qual mantindrà en espera activa regit pel semàfor si aquest té un valor inferior o igual al nul.

V (Semàfor s)
{
 if (!processos_bloquejats())
 s = s+1;
 else
 signal();
}

Aquestes instruccions es poden modificar per evitar l'espera activa, fent que l'operació P dormi al mateix procés que l'executa si no pot decrementar el valor, mentre que l'operació V desperta a un procés que no és qui l'executa. En un pseudollenguatge més comprensible, l'operació P sol denominar-se "wait" o "espera" i l'operació V "signal" o "senyal".

El perquè dels noms d'aquestes funcions, V i P, té el seu origen en la llengua neerlandesa. "Verhogen" significa incrementar i "proberen" provar, encara que Dijkstra va usar la paraula inventada prolaag, que és una combinació deprobeer et Verlagen (intentar decrementar). El valor del semàfor és el nombre d'unitats del recurs que estan disponibles (si només hi ha un recurs, s'utilitza un "semàfor binari" amb els valors 0 i 1).

Si hi ha n recursos, s'inicialitzarà el semàfor al nombre n. Així, cada procés, a l'anar demanant un recurs, comprovarà que el valor del semàfor sigui més gran de 0. Si és així, voldrà dir que hi ha recursos lliures i seguidament adquirirà el recurs i decrementarà el valor del semàfor.

Quan el semàfor arriba a 0, significa que tots els recursos estan essent utilitzats, i els processos que vulguin demanar un recurs hauran d'esperar que el semàfor tingui un valor superior a 0, és a dir: algun dels processos que estan utilitzant els recursos haurà acabat amb ell i incrementarà el semàfor amb un signal o V (s).


Inicia s'utilitza per inicialitzar el semàfor abans que es facin peticions sobre ell, i pren per argument un enter. Quan no hi ha un recurs disponible, l'operació P, atura l'execució quedant en espera activa (o dormint) fins que el valor del semàfor sigui positiu, en aquest cas reclama immediatament decrement. V és l'operació inversa: allibera un recurs després que el procés ha acabat d'usar-lo. Les operacions P i V han de ser indivisibles o atòmiques, és a dir, no poden ser interrompudes enmig de la seva execució.

L'operació V és anomenada a vegades pujar el semàfor (up) i l'operació P es coneix també com baixar el semàfor (down), i també són anomenades signal i wait o deixar anar i prendre.

Per evitar l'espera activa, un semàfor pot tenir associada una cua de processos (normalment una cua FIFO). Si un procés s'efectua una operació P en un semàfor que té valor zero, el procés és bloquejat i afegit a la cua del semàfor. Quan un altre procés incrementa el semàfor mitjançant l'operació V i hi ha processos a la cua associada, s'extreu un d'ells (el primer que va entrar en una cua FIFO) i es reprèn la seva execució.

Usos

[modifica]

Els semàfors s'empren per permetre l'accés a diferents parts de programes (anomenats 'seccions crítiques') on es manipulen variables o recursos que han de ser accedits de manera especial. Segons el valor amb el que són inicialitzats, es permet utilitzar el recurs de forma simultània a més o menys processos.

Un tipus senzill de semàfor és el binari, que pot prendre només els valors 0 i 1. S'inicialitzen en 1 i són usats quan només un procés pot accedir a un recurs a la vegada. Són essencialment el mateix que els mutex. Quan el recurs està disponible, un procés accedeix i decrementa el valor del semàfor amb l'operació P. El valor queda llavors en 0, el que fa que si un altre procés intenta decrementat s'hagi d'esperar. Quan el procés que decrementa el semàfor realitza una operació V, algun procés que estava esperant pot despertar i seguir executant.

Per fer que dos processos s'executin en una seqüència es pot utilitzar un semàfor inicialitzat a 0. El procés que ha d'executar primer en la seqüència realitza l'operació V sobre el semàfor abans del codi que ha de ser executat després de l'altre procés. Aquest executa l'operació P. Si el segon procés en la seqüència és programat per a executar abans que l'altre, en fer P dormirà fins que el primer procés de la seqüència passi per la seva operació V. Aquesta manera d'ús s'anomena senyalitació (signaling), i es fa servir perquè un procés o fil d'execució li faci saber a un altre que alguna cosa ha succeït.

Ús en programació asíncrona

[modifica]

En programació asíncrona, els semàfors també s'utilitzen com a mecanisme de sincronització, però el seu paper principal no és necessàriament protegir una secció crítica entre diversos fils d'execució, sinó limitar el nombre de tasques que poden accedir simultàniament a un recurs o executar una operació determinada. Aquest ús és habitual en entorns basats en bucles d'esdeveniments, on moltes tasques poden quedar suspeses mentre esperen una operació d'entrada/sortida, com ara una resposta de xarxa, una consulta a una base de dades o l'accés a un servei extern.[1]

En biblioteques com asyncio de Python, un semàfor asíncron manté un comptador intern que representa el nombre d'accessos disponibles. Quan una tasca adquireix el semàfor, aquest comptador es decrementa. Si el comptador arriba a zero, les tasques següents que intenten adquirir-lo queden suspeses fins que una altra tasca l'allibera. A diferència d'una espera activa, aquesta suspensió no bloqueja tot el programa, sinó que permet que el bucle d'esdeveniments continuï executant altres tasques pendents.[2]

Aquest mecanisme és útil quan un programa pot iniciar moltes operacions concurrents però necessita evitar una sobrecàrrega. Per exemple, un client HTTP asíncron pot utilitzar un semàfor per limitar el nombre de peticions simultànies a un servidor. De manera semblant, una aplicació pot limitar el nombre de consultes concurrents a una base de dades, el nombre de connexions obertes o la quantitat de tasques que accedeixen a un recurs compartit. D'aquesta manera, el semàfor actua com un regulador de la concurrència i ajuda a evitar la saturació del sistema local o del servei remot.

En aquest context, el semàfor es diferencia d'un cadenat perquè no imposa necessàriament exclusió mútua. Un cadenat acostuma a permetre l'entrada d'una sola tasca a una secció protegida, mentre que un semàfor pot permetre l'accés d'un nombre limitat de tasques. Per això és més adequat per representar recursos amb una capacitat determinada, com un conjunt de connexions disponibles o un límit màxim d'operacions simultànies.

Algunes implementacions també ofereixen semàfors acotats. En aquests casos, el semàfor controla que no s'alliberi més vegades de les que ha estat adquirit, cosa que permet detectar errors de programació en què el comptador podria superar el valor inicial. Aquest comportament és útil quan el semàfor representa una capacitat fixa i no s'hauria d'incrementar dinàmicament durant l'execució del programa.[3]

Exemple d'ús

[modifica]

Els semàfors poden ser usats per a diferents propòsits, entre ells:

  • Implementar forrellats d'exclusió mútua o locks
  • Barreres
  • Permetre a un màxim de N fils d'execució accedir a un recurs, inicialitzant el semàfor en N
  • Notificació. Inicialitzant el semàfor en 0 pot usar-se per a la comunicació entre fils d'execució sobre la disponibilitat d'un recurs.

En el següent exemple es creen i executen n processos que intentaran entrar a la seva secció crítica cada vegada que puguin, i ho aconseguiran sempre d'un en un, gràcies a l'ús del semàfor s inicialitzat a 1. Aquest semàfor té una funció equivalent a un cadenat.

const int n /* nombre de processos */
variable semàfor s; /* declaració de la variable semàfor de valor enter */
Inicia(s, 1) /* Inicialitza un semàfor amb nom s amb valor 1 */

void P(int i) {
 while(true) {
 P(s) /* En semàfors binaris, el correcte és posar un P (s) abans d'entrar a la secció crítica, per restringir l'ús d'aquesta regió del codi */
 /* SECCIÓ CRÍTICA */
 V(s) /* Després de la secció crítica, tornem a posar el semàfor a 1 perquè un altre procés pugui usar */
 /* RESTA DEL CODI */
 }
}
int main(){
 Començar-processos(P(1), P(2),..., P(n));
}

Referències

[modifica]
  1. «Synchronization Primitives» (en anglès). [Consulta: 23 maig 2026].
  2. «asyncio.Semaphore». Python Software Foundation. [Consulta: 23 maig 2026].
  3. Fowler, Matthew. Python Concurrency with asyncio. Manning Publications, 2022, p. 276-279. ISBN 9781617298660.

Vegeu també

[modifica]

Enllaços externs

[modifica]