CATEGORII DOCUMENTE |
Fiecare proces are la crearea sa un singur fir de executie si pana nu demult acest lucru parea suficient. Pentru cazurile in care un singur de fir de executie nu mai este de ajuns avem in cazul sistemelor de operare mai multe solutii.
Prima solutie este oferita de utilizarea functie:
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,
SIZE_T dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId
Parametrii functiei au urmatoarea utilizare:
In caz de esec functia intoarce NULL, altfel va intoarce descriptorul firului. In MSDN Library puteti gasi urmatorul exemplu de utilizare a functiei:
#include <windows.h>
#include <conio.h>
DWORD WINAPI ThreadFunc( LPVOID lpParam )
void main( void )
else
Firul principal transmite valoarea 1 noului fir, care o afiseaza. Terminarea unui fir nu presupune si stergerea obiectului asociat firului de catre sistemul de operare. Un obiect thread este sters atunci cand si ultimul descriptor (HANDLE) catre fir este inchis, acest lucru realizandu-l apelul:
CloseHandle( hThread );
In cazul in care doriti sa folositi functii din bibliotecile C statice, utilizarea CreateThread nu este prea indicata deoarece duce la mici pierderi de memorie la terminarea fiecarui fir. In acest caz este recomandata utilizarea celei de-a doua metode, oferita de functiile:
uintptr_t _beginthread(
void( __cdecl *start_address )( void * ),
unsigned stack_size,
void *arglist
uintptr_t _beginthreadex(
void *security,
unsigned stack_size,
unsigned ( __stdcall *start_address )( void * ),
void *arglist,
unsigned initflag,
unsigned *thrdaddr
Parametrii:
In caz de succes ambele functii intorc un descriptor pentru noul fir creat. In caz de eroare _beginthread intoarce -1L, iar cealalta functie intoarce 0.
Atunci cand un fir se termina se apeleaza automat _endthread, respectiv _endthreadex. Apelul endthread inchide automat descriptorul asociat cu firul, lucru pe care nu-l face _endthreadex.
Atunci cand doriti sa compilati un program care foloseste aceste functii va trebui sa folositi o biblioteca multifir. O cale prin care puteti realiza acest lucru este data de modificarea setarii 'Use run-time library'[3] astfel incat sa contina Multithreaded in nume.
#include <stdio.h>
#include <process.h>
#include <windows.h>
volatile running;
void fir(void* numar);
void main()
while(running);
void fir( void* numar)
Exemplul de mai sus are mai multe bug-uri, unul dintre ele fiind folosirea abuziva a calificatorului volatile. Obiectele declarate ca volatile nu sunt folosite in optimizari de catre compilator deoarece valoarea lor poate fi modificata de catre altcineva. Sistemul va citi de fiecare data valoarea curenta a obiectului volatil chiar daca aceasta se gasea deja intr-un registru si valoarea volatila va fi scrisa imediat dupa asignare. Dar nimeni nu garanteaza ca operatiile sunt atomice. Iata cum arata incrementarea valorii running in asamblare:
mov eax,[running (0042d3f0)]
add eax,1
mov [running (0042d3f0)],eax
Acest modificator este potrivit numai pentru instructiuni in care variabila respectiva este numai folosita sau numai atribuita.
Un alt bug, si nu singurul, continut de exemplul anterior este dat de faptul ca foloseste asteptarea continua pentru sincronizare. Din fericire, in Windows avem o mare varietate de mecanisme de sincronizare. In continuare, o sa fie prezentate doar trei dintre ele: evenimente, mutexuri si semafoare.
Un obiect eveniment poate avea doar doua stari: semnalizat sau nesemnalizat. Este util in cazul in care doriti sa transmiteti unui fir un anumit semnal care sa-i indice ca un anumit eveniment a avut loc, avand avantajul fata de variabilele conditie ca pierderea respectivului semnal este mult mai greu de obtinut printr-o programare defectuoasaJ
Crearea unui obiect eveniment se face cu ajutorul functiei[4]:
HANDLE CreateEvent(
LPSECURITY_ATTRIBUTES lpEventAttributes,
BOOL bManualReset,
BOOL bInitialState,
LPCTSTR lpName
In caz de succes functia intoarce un descriptor pentru eveniment. In caz de esec intoarce NULL, iar daca evenimentul este deja creat intoarce descriptorul respectivului eveniment si GetLastError va intoarce ERROR_ALREADY_EXISTS.
Pentru a obtine un descriptor pentru un eveniment deja creat de un alt fir dintr-un alt proces, avem functia:s
HANDLE OpenEvent(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
LPCTSTR lpName
In caz de succes functia intoarce un descriptor al evenimentului, altfel intoarce NULL.
Pentru a semnaliza un obiect se foloseste functia SetEvent:
BOOL SetEvent(
HANDLE hEvent
Functia intoarce in caz de succes o valoare diferita de zero sau zero in caz de esec.
Daca evenimentul este resetabil manual, din cand in cand este poate util sa-i schimbati starea in nesemnalizat, folosind functia ResetEvent:
BOOL ResetEvent(
HANDLE hEvent
Functia intoarce in caz de succes o valoare diferita de zero sau zero in caz de esec.
Pentru a astepta unul sau mai multe evenimente se vor folosi functiile de asteptare.
Pentru a crea un obiect mutex avem functia:
HANDLE CreateMutex(
LPSECURITY_ATTRIBUTES lpMutexAttributes,
BOOL bInitialOwner,
LPCTSTR lpName
In caz de succes functia intoarce un descriptor pentru mutex. In caz de esec intoarce NULL, iar daca mutexul este deja creat intoarce descriptorul respectivului mutex si GetLastError va intoarce ERROR_ALREADY_EXISTS.
Pentru a obtine un descriptor pentru un mutex deja creat de un alt fir dintr-un alt proces, avem functia:s
HANDLE OpenMutex(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
LPCTSTR lpName
In caz de succes functia intoarce un descriptor al mutexului, altfel intoarce NULL.
Pentru a bloca un mutex se foloseste una din functiile de asteptare.
Pentru a elibera un mutex:
BOOL ReleaseMutex(
HANDLE hMutex
Functia intoarce in caz de succes o valoare diferita de zero sau zero in caz de esec.
Pentru a crea sau pentru a obtine un descriptor pentru un semafor avem functiile:
HANDLE CreateSemaphore(
LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
LONG lInitialCount,
LONG lMaximumCount,
LPCTSTR lpName
HANDLE OpenSemaphore(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
LPCTSTR lpName
Parametrii au semnificatie asemanatoare cu cei ai functiilor pentru evenimente si mutexuri. Apar in plus doi parametrii care specifica valoarea initiala a semaforului si valoarea maxima pe care o poate avea acesta.
Pentru a creste contorul unui semafor cu o anumita valoare avem functia ReleaseSemaphore:
BOOL ReleaseSemaphore(
HANDLE hSemaphore,
LONG lReleaseCount,
LPLONG lpPreviousCount
Intoarce zero in caz de esec si o valoare diferita de zero altfel.
In aceasta sectiune vor fi prezentate doar cateva din functiile de asteptare.
Pentru a astepta dupa un singur obiect, avem:
DWORD WaitForSingleObject(
HANDLE hHandle,
DWORD dwMilliseconds
Daca functia reuseste, valoarea intoarsa va indica ce eveniment a facut ca functia sa termine asteptarea. Poate avea una din valorile:
In caz de esec intoarce WAIT_FAILED.
Functia blocheaza firul apelant pana cand expira intervalul specificat sau pana cand obiectul specificat devine semnalizat.
Pentru a astepta mai multe obiecte:
DWORD WaitForMultipleObjects(
DWORD nCount,
const HANDLE* lpHandles,
BOOL bWaitAll,
DWORD dwMilliseconds
In caz de succes, valoarea intoarsa va indica ce anume a cauzat terminarea asteptarii:
In caz de esec, functia intoarce WAIT_FAILED.
Exemplu de folosire a evenimentelor, mutex-urilor si a functiilor de asteptare:
#include <stdio.h>
#include <process.h>
#include <memory.h>
#include <malloc.h>
#include <stdlib.h>
#include <windows.h>
HANDLE evast[4];
void aduna(void* args);
void* puneArgumente(int v[], int nrElemente, int fir, int nrFire)
int suma = 0;
HANDLE hMutex1, hMutex2;
void main();
int n = 9;
// Create a mutex with no initial owner.
hMutex1 = CreateMutex(
NULL, // no security attributes
FALSE, // initially not owned
'mutex pentru printf'); // name of mutex
if (hMutex1 == NULL)
hMutex2 = CreateMutex(
NULL, // no security attributes
FALSE, // initially not owned
'mutex pentru suma'); // name of mutex
if (hMutex2 == NULL)
int nrFire = 3;
for( int i=0; i< nrFire; i++)
_beginthread(aduna, 0, args);
}
WaitForMultipleObjects(
nrFire, // number of objects in array
evast, // array of objects
TRUE, // wait for all
INFINITE);
printf('suma este %dn', suma);
void aduna(void* args)
In afara cazului in care este specificat altfel, termenul 'Windows' se refera la toate sistemele de operare Microsoft de la Windows 95 pana la Windows XP.
Politica de confidentialitate | Termeni si conditii de utilizare |
Vizualizari: 1564
Importanta:
Termeni si conditii de utilizare | Contact
© SCRIGROUP 2024 . All rights reserved