POSIX Threads Programming AL
POSIX Threads Programming AL
Abstract
Në arkitekturat multiprocesoresh me memorie të përbashkët, thread-et mund të përdoren për
të zbatuar paralelizmin. Historikisht, shitësit e pajisjeve kanë zbatuar versionet e tyre të pronarit
të thread-eve, duke e bërë transportueshmërinë një shqetësim për zhvilluesit e softuerit. Për
sistemet UNIX, një ndërfaqe programimi e standardizuar e fijeve të gjuhës C është specifikuar
nga standardi IEEE POSIX 1003.1c. Implementimet që i përmbahen këtij standardi quhen fije
POSIX ose Pthreads.
Tutoriali fillon me një hyrje në konceptet, motivimet dhe konsideratat e dizajnit për përdorimin
e Pthreads. Më pas mbulohet secila nga tre klasat kryesore të rutinave në API-në e Pthreads:
Menaxhimi i Thread, Variablat Mutex dhe Variablat e Kushteve. Kodet e shembujve përdoren
kudo për të demonstruar se si të përdoren shumica e rutinave Pthreads të nevojshme nga një
programues i ri Pthreads. Tutoriali përfundon me një diskutim të specifikave të LLNL dhe si të
përzieni MPI me threads. Përfshihet gjithashtu një ushtrim laboratori, me kode të shumta
shembujsh (Gjuha C).
Niveli/Kushtet paraprake: Ky tutorial është ideal për ata që janë të rinj në programimin paralel
me threads. Kërkohet një kuptim bazë i programimit paralel në C. Për ata që nuk janë të njohur
me programimin paralel në përgjithësi, materiali i mbuluar në EC3500: Hyrje në llogaritjen
paralele do të ishte i dobishëm.
Pthreads Overview
What is a Thread?
Teknikisht, një thread përkufizohet si një rrjedhë e pavarur instruksionesh që mund të
planifikohet të ekzekutohet si e tillë nga sistemi operativ. Por çfarë do të thotë kjo?
Për zhvilluesin e softuerit, koncepti i një "procedure" që funksionon në mënyrë të pavarur nga
programi i saj kryesor mund të përshkruajë më së miri një thread.
Për të shkuar një hap më tej, imagjinoni një program që përmban një sërë procedurash. Pastaj
imagjinoni që të gjitha këto procedura të mund të planifikohen të ekzekutohen njëkohësisht
dhe/ose në mënyrë të pavarur nga sistemi operativ. Kjo do të përshkruante një program "me
shumë fije".
Si realizohet kjo?
Para se të kuptoni një thread, së pari duhet të kuptoni një proces UNIX. Një proces krijohet nga
sistemi operativ dhe kërkon një sasi të mjaftueshme të "shpenzimeve të përgjithshme".
Proceset përmbajnë informacion në lidhje me burimet e programit dhe gjendjen e ekzekutimit
të programit, duke përfshirë:
• Informacioni identifikues ([Link]. ID-ja e procesit dhe ID-ja e përdoruesit)
• Variablat e mjedisit
• Drejtoria e punës
• Udhëzimet e programit
• Gjendja e CPU-së ([Link]. regjistrat, informacioni i defektit)
• Shpërndarja e memories (stivë dhe grumbull)
Page 1 of 50
• Përshkruesit e skedarëve
• Veprimet sinjalizuese
• Bibliotekat e përbashkëta
• Informacione të ndërlidhura me komunikimin ndërmjet proceseve (siç janë radhët e
mesazheve, tubacionet, semaforët ose memoria e përbashkët).
Unix process
Page 2 of 50
Threads within a unix process
Thread-et përdorin dhe ekzistojnë brenda këtyre burimeve të procesit, por janë në gjendje të
planifikohen nga sistemi operativ dhe të ekzekutohen si entitete të pavarura. Për ta arritur këtë,
thread-et mbajnë vetëm burimet thelbësore që u mundësojnë atyre të ekzistojnë si kod i
ekzekutueshëm, si [Link].
• Treguesi i stivës
• Regjistrat
• Vetitë e planifikimit (të tilla si politika ose përparësia)
• Një grup sinjalesh në pritje dhe të bllokuara
• Të dhëna specifike për fije
Pra, në përmbledhje, në mjedisin UNIX një thread:
• Ekziston brenda një procesi dhe përdor burimet e procesit
• Ka fluksin e vet të pavarur të kontrollit për sa kohë që ekziston procesi i tij prind dhe OS
e mbështet atë
• Dublikon vetëm burimet thelbësore që i nevojiten për të planifikuar në mënyrë të
pavarur, duke e bërë atë të lehtë
• Mund të ndajë burimet e procesit me temat e tjera
Sepse temat brenda të njëjtit proces ndajnë burimet:
• Ndryshimet e bëra nga një thread në burimet e përbashkëta të sistemit (siç është
mbyllja e një skedari) do të shihen nga të gjitha temat e tjera
• Dy tregues që kanë të njëjtën vlerë tregojnë për të njëjtat të dhëna
• Leximi dhe shkrimi në të njëjtat vendndodhje memorie është i mundur, dhe për këtë
arsye kërkon sinkronizim të qartë nga programuesi
Page 3 of 50
What are Pthreads?
Pthreads Overview: What are Pthreads?
Historikisht, shitësit e pajisjeve kanë zbatuar versionet e tyre të pronarit të temave. Këto
zbatime ndryshonin thelbësisht nga njëra-tjetra duke e bërë të vështirë për programuesit
zhvillimin e aplikacioneve portative me fileto.
Për të përfituar plotësisht nga aftësitë e ofruara nga thread-et, kërkohej një ndërfaqe
programimi e standardizuar.
• Për sistemet UNIX, kjo ndërfaqe është specifikuar nga standardi IEEE POSIX 1003.1c
(1995).
• Implementimet që i përmbahen këtij standardi referohen si threads POSIX, ose
Pthreads.
• Shumica e shitësve të pajisjeve tani ofrojnë Pthread përveç API-ve të tyre të pronarit.
Standardi POSIX ka vazhduar të zhvillohet dhe t'i nënshtrohet rishikimeve, duke përfshirë
specifikimin Pthreads.
Disa lidhje të dobishme:
• [Link]/findstds/standard/[Link]
• [Link]/austin/papers/posix_faq.html
Pthread-et përcaktohen si një grup i llojeve të programimit të gjuhës C dhe thirrjeve
procedurash, të zbatuara me një skedar pthread.h header/include dhe një bibliotekë thread -
megjithëse kjo bibliotekë mund të jetë pjesë e një biblioteke tjetër, si libc, në disa
implementime.
Page 4 of 50
Why Pthreads?
Pthreads Overview: Why Pthreads?
Lightweight:
Kur krahasohen me proceset, thread-et mund të krijohen dhe menaxhohen me shumë më pak
shpenzime nga sistemi operativ.
Për shembull, tabela e mëposhtme krahason rezultatet e kohës për nënprogramin fork() dhe
nënprogramin pthread_create(). Kohët pasqyrojnë 50,000 krijime procesesh/fijesh, u kryen me
mjetin e time dhe njësitë janë në sekonda, pa flamuj optimizimi.
Shënim: mos prisni që sistemi dhe koha e përdoruesit të shtohen në kohë reale, sepse këto janë
sisteme SMP me CPU/bërthama të shumta që punojnë për problemin në të njëjtën kohë. Në
rastin më të mirë, këto janë përafrime të ekzekutuara në makinat lokale, të kaluara dhe të
tashme.
fork() pthread_create()
Platform
real user sys real user sys
Intel 2.6 GHz Xeon E5-2670 (16 cores/node) 8.1 0.1 2.9 0.9 0.2 0.3
Intel 2.8 GHz Xeon 5660 (12 cores/node) 4.4 0.4 4.3 0.7 0.2 0.5
AMD 2.3 GHz Opteron (16 cores/node) 12.5 1.0 12.5 1.2 0.2 1.3
AMD 2.4 GHz Opteron (8 cores/node) 17.6 2.2 15.7 1.4 0.3 1.3
IBM 4.0 GHz POWER6 (8 cpus/node) 9.5 0.6 8.8 1.6 0.1 0.4
IBM 1.9 GHz POWER5 p5-575 (8 64.2 30.7 27.6 1.7 0.6 1.1
cpus/node)
IBM 1.5 GHz POWER4 (8 cpus/node) 104.5 48.6 47.2 2.1 1.0 1.5
INTEL 2.4 GHz Xeon (2 cpus/node) 54.9 1.5 20.8 1.6 0.7 0.9
INTEL 1.4 GHz Itanium2 (4 cpus/node) 54.5 1.1 22.2 2.0 1.2 0.6
Page 5 of 50
Për Pthreads nuk kërkohet një kopje e memories së ndërmjetme sepse thread-et ndajnë të
njëjtën hapësirë adresash brenda një procesi të vetëm. Nuk ka transferim të të dhënave, në
vetvete. Mund të jetë po aq efikas sa thjesht kalimi i një treguesi.
Në skenarin më të keq, komunikimet Pthreads bëhen më shumë një problem i gjerësisë së brezit
të cache-to-CPU ose memorie-to-CPU. Këto shpejtësi janë shumë më të larta se komunikimet
me memorie të përbashkët MPI.
Arsyet e tjera të zakonshme:
Aplikacionet e filetuara ofrojnë përfitime të mundshme të performancës dhe avantazhe praktike
ndaj aplikacioneve jo të filetuara në disa mënyra të tjera:
• Mbivendosja e punës së CPU-së me I/O: Për shembull, një program mund të ketë
seksione ku po kryen një operacion të gjatë I/O. Ndërsa një thread pret që një thirrje
sistemi I/O të përfundojë, puna intensive e CPU-së mund të kryhet nga thread të tjerë.
• Planifikimi me përparësi/kohë reale: detyrat që janë më të rëndësishme mund të
planifikohen për të zëvendësuar ose ndërprerë detyrat me prioritet më të ulët.
• Trajtimi asinkron i ngjarjeve: detyra të cilat mund të ndërlidhen me ngjarje të shërbimit
me frekuencë dhe kohëzgjatje të papërcaktuar. Për shembull, një server në internet
mund të transferojë të dhëna nga kërkesat e mëparshme dhe të menaxhojë ardhjen e
kërkesave të reja.
Një shembull i përsosur është shfletuesi tipik i internetit, ku shumë detyra me përparësi të
ndryshme duhet të ndodhin në të njëjtën kohë, dhe kështu mund të ndërlidhen.
Një shembull tjetër i mirë është një sistem operativ modern, i cili përdor gjerësisht fijet. Një
pamje nga ekrani i sistemit operativ MS Windows dhe aplikacioneve që përdorin temat është
paraqitur më poshtë.
Page 6 of 50
Designing Threaded Programs
Pthreads Overview: Designing Threaded Programs
Parallel Programming
Në makineritë moderne me shumë bërthama, thread-et janë të përshtatshme në mënyrë ideale
për programim paralel dhe çfarëdo që vlen për programimin paralel në përgjithësi, vlen edhe
për programet paralele thread.
Ka shumë konsiderata për hartimin e programeve paralele, si [Link].
• Modeli i programimit paralel
• Problem me ndarjen
• Balancimi i ngarkesës
• Komunikimet
• Varësia e të dhënave
• Sinkronizimi dhe kushtet e garës
• Probleme me kujtesën
• Probleme me I/O
• Kompleksiteti i programit
• Përpjekja/kostot/koha e programuesit
Mbulimi i këtyre temave është përtej qëllimit të këtij tutoriali, megjithatë lexuesit e interesuar
mund të marrin një përmbledhje të shpejtë në tutorialin "Hyrja në informatikë paralele".
Megjithatë, në përgjithësi, në mënyrë që një program të përfitojë nga Pthreads, ai duhet të jetë
në gjendje të organizohet në detyra diskrete, të pavarura, të cilat mund të ekzekutohen
njëkohësisht. Për shembull, nëse dy rutina mund të ndërrohen, ndërlidhen dhe/ose
mbivendosen në kohë reale, ato janë kandidate për fillesë.
Page 7 of 50
Programet që kanë karakteristikat e mëposhtme mund të jenë të përshtatshme për thread:
• Blloko për pritjet potencialisht të gjata I/O
• Përdorni shumë cikle CPU në disa vende, por jo në të tjera
• Duhet t'u përgjigjet ngjarjeve asinkrone
• Disa punë janë më të rëndësishme se punët e tjera (ndërprerjet me përparësi)
Ekzistojnë disa modele të zakonshme për programet e filetuara:
• Menaxheri/punëtor: një menaxher me një fije të vetme cakton punën te fijet e tjera,
punëtorët. Në mënyrë tipike, menaxheri trajton të gjitha të dhënat dhe ndan punën për
detyrat e tjera. Të paktën dy forma të modelit të menaxherit/punëtorit janë të
zakonshme: grupi statik i punëtorëve dhe grupi dinamik i punëtorëve.
• Pipeline: një detyrë ndahet në një seri nënoperacionesh, secila prej të cilave trajtohet
në sekuencë, por njëkohësisht, nga një fill i ndryshëm. Një linjë montimi automobilistik e
përshkruan më së miri këtë model.
• Peer: i ngjashëm me modelin e menaxherit/punëtorit, por pasi filli kryesor krijon fije të
tjera, ai merr pjesë në punë.
Page 8 of 50
Shared Memory Model
Të gjitha thread-et kanë akses në të njëjtën memorie globale, të përbashkët, por temat kanë
gjithashtu të dhënat e tyre private. Programuesit janë përgjegjës për sinkronizimin (dhe
mbrojtjen) e aksesit në të dhënat e përbashkëta globale.
Page 9 of 50
Implikimi për përdoruesit e rutinave të bibliotekave të jashtme është se nëse nuk jeni 100% i
sigurt se rutina është e sigurt në fije, atëherë ju merrni shanset tuaja me problemet që mund të
lindin.
Rekomandim: Kini kujdes nëse aplikacioni juaj përdor biblioteka ose objekte të tjera që nuk
garantojnë në mënyrë të qartë sigurinë e fijeve. Kur dyshoni, supozoni se ato nuk janë të sigurta
për fije derisa të provohet e kundërta. Kjo mund të bëhet duke “serializuar” thirrjet drejt rutinës
së pasigurt, etj.
Kufijtë e fijeve
Edhe pse Pthreads API është një standard ANSI/IEEE, implementimet mund, dhe zakonisht
ndryshojnë, në mënyra që nuk specifikohen nga standardi. Për shkak të kësaj, një program që
funksionon mirë në një platformë, mund të dështojë ose të prodhojë rezultate të gabuara në një
platformë tjetër. Për shembull, numri maksimal i thread-ve i lejuar dhe madhësia e parazgjedhur
e stivës së fillit janë dy kufij të rëndësishëm për t'u marrë parasysh kur hartoni programin tuaj.
Disa kufij të fijeve diskutohen më në detaje më vonë në këtë tutorial.
Page 10 of 50
Nënprogramet që përbëjnë API-në e Pthreads mund të grupohen joformalisht në katër grupe
kryesore:
• Menaxhimi i temave: Rutina që funksionojnë drejtpërdrejt në thread - krijimi,
shkëputja, bashkimi, etj. Ato përfshijnë gjithashtu funksione për të vendosur/kërkuar
atributet e fijeve (të bashkueshme, planifikimi etj.)
• Mutexes: Rutina që merren me primitive sinkronizimi mutex (shkurt për përjashtim të
ndërsjellë). Këto funksione lejojnë krijimin, shkatërrimin, kyçjen dhe zhbllokimin e
mutexes. Ato plotësohen nga funksionet e atributeve që vendosin ose modifikojnë
atributet e lidhura me mutexes.
• Variablat e gjendjes: Rutinat që adresojnë komunikimet ndërmjet temave që ndajnë
një mutex. Bazuar në kushtet e specifikuara nga programuesi. Ky grup përfshin funksione
për të krijuar, shkatërruar, pritur dhe sinjalizuar bazuar në vlerat e specifikuara të
variablave. Funksionet për të vendosur/kërkuar atributet e ndryshoreve të kushtit janë
gjithashtu të përfshira.
• Sinkronizimi: Rutinat që menaxhojnë bllokimet dhe barrierat e leximit/shkrimit.
Konventat e emërtimit: Të gjithë identifikuesit në bibliotekën e threads fillojnë me pthread_.
Disa shembuj janë paraqitur më poshtë.
pthread_mutex_ Mutexes
Koncepti i objekteve opake përshkon dizajnin e API-së. Thirrjet bazë funksionojnë për të krijuar
ose modifikuar objekte opake - objektet opake mund të modifikohen me thirrje për funksione të
atributeve, të cilat kanë të bëjnë me atributet opake.
Page 11 of 50
API Pthreads përmban rreth 100 nënprograme. Ky tutorial do të fokusohet në një nëngrup prej
tyre - veçanërisht, ato që kanë më shumë gjasa të jenë të dobishme menjëherë për
programuesin fillestar të Pthreads.
Për transportueshmëri, skedari i kokës pthread.h duhet të përfshihet në çdo skedar burimi duke
përdorur bibliotekën Pthreads.
Standardi aktual POSIX është përcaktuar vetëm për gjuhën C. Programuesit e Fortran mund të
përdorin mbështjellës rreth thirrjeve të funksionit C. Disa përpilues të Fortran mund të ofrojnë
një API të threads Fortran.
Një numër librash të shkëlqyer rreth Pthreads janë në dispozicion. Disa prej tyre janë renditur
në seksionin e References të këtij tutoriali.
Page 12 of 50
Thread Management
Creating and Terminating Threads
Thread Management: Creating and Terminating
Threads
Routines:
pthread_create(thread, attr, start_routine, arg)
pthread_exit(status)
pthread_cancel(thread)
pthread_attr_init(attr)
pthread_attr_destroy(attr)
Creating Threads:
Fillimisht, programi juaj kryesor () përbëhet nga një fije e vetme, e paracaktuar. Të
gjitha temat e tjera duhet të krijohen në mënyrë eksplicite nga programuesi.
pthread_create krijon një thread të ri dhe e bën atë të ekzekutueshëm. Kjo rutinë
mund të thirret çdo herë nga kudo brenda kodit tuaj.
pthread_create argumentet:
• thread: Një identifikues i errët, unik për fillin e ri të kthyer nga nënprogrami.
• attr: Një objekt atribut i errët që mund të përdoret për të vendosur atributet
e fillit. Ju mund të specifikoni një objekt të atributeve të fillit, ose NULL për
vlerat e paracaktuara.
• start_rutine: rutina C që thread do të ekzekutojë sapo të krijohet.
• arg: Një argument i vetëm që mund t'i kalohet fillimit_rutinës. Duhet të
kalohet me referencë si (void *). NULL mund të përdoret nëse nuk duhet të
kalohet asnjë argument.
Numri maksimal i thread-eve që mund të krijohen nga një proces varet nga zbatimi.
Programet që përpiqen të tejkalojnë kufirin mund të dështojnë ose të prodhojnë
rezultate të gabuara.
Thread limits
Shembujt vijues tregojnë se si të kërkoni dhe vendosni kufirin e fillesave të zbatimit
tuaj në Linux. Së pari ne kërkojmë kufijtë e paracaktuar (të buta) dhe më pas
vendosim numrin maksimal të proceseve (përfshirë thread-ët) në kufirin e vështirë.
Më pas verifikojmë që kufiri është tejkaluar.
Page 13 of 50
bash / ksh / sh example
$ ulimit -a
core file size (blocks, -c) 16
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 255956
max locked memory (kbytes, -l) 64
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) unlimited
cpu time (seconds, -t) unlimited
max user processes (-u) 1024
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
$ ulimit -Hu
7168
$ ulimit -u 7168
$ ulimit -a
core file size (blocks, -c) 16
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 255956
max locked memory (kbytes, -l) 64
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) unlimited
cpu time (seconds, -t) unlimited
max user processes (-u) 7168
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
tcsh/csh example
% limit
cputime unlimited
filesize unlimited
datasize unlimited
stacksize unlimited
Page 14 of 50
coredumpsize 16 kbytes
memoryuse unlimited
vmemoryuse unlimited
descriptors 1024
memorylocked 64 kbytes
maxproc 1024
% limit
cputime unlimited
filesize unlimited
datasize unlimited
stacksize unlimited
coredumpsize 16 kbytes
memoryuse unlimited
vmemoryuse unlimited
descriptors 1024
memorylocked 64 kbytes
maxproc 7168
Pasi të krijohen, temat janë bashkëmoshatarë dhe mund të krijojnë fije të tjera. Nuk
ka asnjë hierarki ose varësi të nënkuptuar midis threads.
Thread Attributes:
• Gjendje e shkëputur ose e bashkuar
• Planifikimi i trashëgimisë
• Politika e planifikimit
• Parametrat e planifikimit
Page 15 of 50
• Planifikimi i fushëveprimit të grindjeve
• Madhësia e stivës
• Adresa e stivës
• Madhësia e mbrojtëses së pirgut (mbushjes).
• Disa nga këto atribute do të diskutohen më vonë.
Page 16 of 50
• Duke thirrur në mënyrë eksplicite main() pthread_exit() si gjënë e fundit që
bën, main() do të bllokohet dhe do të mbahet i gjallë për të mbështetur
thread-et që ka krijuar derisa të mbarojnë.
Example: Pthread Creation and Termination
This simple example code creates 5 threads with the pthread_create() routine. Each
thread prints a “Hello World!” message. Then, it terminates with a call
to pthread_exit().
#include <pthread.h>
#include <stdio.h>
#define NUM_THREADS 5
Output:
Page 17 of 50
In main: creating thread 0
In main: creating thread 1
Hello World! It's me, thread #0!
In main: creating thread 2
Hello World! It's me, thread #1!
Hello World! It's me, thread #2!
In main: creating thread 3
In main: creating thread 4
Hello World! It's me, thread #3!
Hello World! It's me, thread #4!
long taskids[NUM_THREADS];
Page 18 of 50
Creating thread 1
Creating thread 2
Creating thread 3
Creating thread 4
Creating thread 5
Creating thread 6
Creating thread 7
Thread 0: English: Hello World!
Thread 1: French: Bonjour, le monde!
Thread 2: Spanish: Hola al mundo
Thread 3: Klingon: Nuq neH!
Thread 4: German: Guten Tag, Welt!
Thread 5: Russian: Zdravstvytye, mir!
Thread 6: Japan: Sekai e konnichiwa!
Thread 7: Latin: Orbis, te saluto!
Example 2 - Thread Argument Passing
Ky shembull tregon se si të vendosni/kaloni argumente të shumta nëpërmjet një
strukture. Çdo thread merr një shembull unik të strukturës.
struct thread_data{
int thread_id;
int sum;
char *message;
};
Page 19 of 50
See the source code.
Output:
Creating thread 0
Creating thread 1
Creating thread 2
Creating thread 3
Creating thread 4
Creating thread 5
Creating thread 6
Creating thread 7
Thread 0: English: Hello World! Sum=0
Thread 1: French: Bonjour, le monde! Sum=1
Thread 2: Spanish: Hola al mundo Sum=3
Thread 3: Klingon: Nuq neH! Sum=6
Thread 4: German: Guten Tag, Welt! Sum=10
Thread 5: Russian: Zdravstvytye, mir! Sum=15
Thread 6: Japan: Sekai e konnichiwa! Sum=21
Thread 7: Latin: Orbis, te saluto! Sum=28
Example 3 - Thread Argument Passing (Incorrect)
Ky shembull kryen kalimin e gabuar të argumentit. Ai kalon adresën e ndryshores
t, e cila është hapësirë e përbashkët e memories dhe e dukshme për të gjitha thread-
ët. Ndërsa cikli përsëritet, vlera e këtij lokacioni memorie ndryshon, ndoshta
përpara se thread-et e krijuara të mund ta qasen atë.
int rc;
long t;
Page 20 of 50
Creating thread 0
Creating thread 1
Creating thread 2
Creating thread 3
Creating thread 4
Creating thread 5
Creating thread 6
Creating thread 7
Hello from thread 140737488348392
Hello from thread 140737488348392
Hello from thread 140737488348392
Hello from thread 140737488348392
Hello from thread 140737488348392
Hello from thread 140737488348392
Hello from thread 140737488348392
Hello from thread 140737488348392
Page 21 of 50
Nënrutina pthread_join() bllokon thread-in thirrës derisa thread-i i specifikuar të
përfundojë.
Programuesi është në gjendje të marrë statusin e përfundimit të fillit të synuar nëse
është specifikuar në thirrjen e thread-it të synuar në pthread_exit().
Një fije mund të bashkohet vetëm një herë. Është një gabim logjik të tentosh
bashkime të shumta në të njëjtën fije.
Dy metoda të tjera sinkronizimi, mutexes dhe variablat e gjendjes, do të diskutohen
më vonë.
Mund të bashkohet apo jo?
Kur krijohet një thread, një nga atributet e tij përcakton nëse është i bashkueshëm
apo i shkëputur. Mund të bashkohen vetëm temat që janë krijuar si të bashkuara.
Nëse një fije krijohet si e shkëputur, ajo nuk mund të bashkohet kurrë.
Drafti përfundimtar i standardit POSIX specifikon që thread-et duhet të krijohen si
të bashkuara.
Për të krijuar në mënyrë eksplicite një thread si të bashkuar ose të shkëputur,
përdoret argumenti attr në rutinën pthread_create(). Procesi tipik me 4 hapa është:
Page 22 of 50
Detaching
Rutina pthread_detach() mund të përdoret për të shkëputur në mënyrë eksplicite
një thread edhe pse është krijuar si e bashkuar.
Nuk ka asnjë rutinë të kundërt.
Recommendations:
Nëse një fill kërkon bashkim, merrni parasysh krijimin e tij në mënyrë eksplicite si
të bashkueshme. Kjo siguron transportueshmëri pasi jo të gjitha implementimet
mund të krijojnë thread si të bashkuar sipas parazgjedhjes.
Nëse e dini paraprakisht se një fill nuk do të duhet kurrë të bashkohet me një fije
tjetër, merrni parasysh ta krijoni atë në një gjendje të shkëputur, pasi kjo mund të
zvogëlojë shpenzimet e përgjithshme.
#define NUM_THREADS 4
Page 23 of 50
/* Initialize and set thread detached attribute */
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
Page 24 of 50
Stack Management
Routines:
pthread_attr_getstacksize(attr, stacksize)
pthread_attr_setstacksize (attr, addr, stacksize)
pthread_attr_getstackaddr(attr, stackaddr)
pthread_attr_setstackaddr(attr, stackaddr)
Preventing Stack Problems:
Standardi POSIX nuk dikton madhësinë e pirgut të një thread. Kjo varet nga zbatimi
dhe ndryshon.
Tejkalimi i kufirit të parazgjedhur të stivës është shpesh shumë i lehtë për t'u bërë,
me rezultatet e zakonshme: përfundimi i programit dhe/ose të dhëna të
korruptuara.
Programet e sigurta dhe të lëvizshme nuk varen nga kufiri i parazgjedhur i stivës,
por në vend të kësaj, shpërndajnë në mënyrë eksplicite stack të mjaftueshëm për
çdo thread duke përdorur rutinën pthread_attr_setstacksize.
Rutinat pthread_attr_getstackaddr dhe pthread_attr_setstackaddr mund të përdoren
nga aplikacionet në një mjedis ku pirgu për një thread duhet të vendoset në një
zonë të veçantë të memories.
Disa shembuj praktikë në LC:
Madhësia e parazgjedhur e stivës së fillit ndryshon shumë. Madhësia maksimale që
mund të merret gjithashtu ndryshon shumë dhe mund të varet nga numri i fijeve
për nyje.
Të dyja arkitekturat e kaluara dhe ato të tanishme tregohen për të demonstruar
ndryshimin e gjerë në madhësinë e parazgjedhur të stivës së fijeve.
Node #CPUs Memory (GB) Default Size
Architecture (bytes)
Page 25 of 50
Node #CPUs Memory (GB) Default Size
Architecture (bytes)
#define NTHREADS 4
#define N 1000
#define MEGEXTRA 1000000
pthread_attr_t attr;
tid = (long)threadid;
pthread_attr_getstacksize(&attr, &mystacksize);
printf("Thread %ld: stack size = %li bytes \n", tid, mystacksize);
for (i = 0; i < N; i++) {
for (j = 0; j < N; j++) {
A[i][j] = ((i * j) / 3.452) + (N - i);
}
}
pthread_exit(NULL);
}
pthread_attr_init(&attr);
pthread_attr_getstacksize(&attr, &stacksize);
printf("Default stack size = %li\n", stacksize);
Page 26 of 50
stacksize = sizeof(double)*N*N+MEGEXTRA;
printf("Amount of stack needed per thread = %li\n", stacksize);
pthread_attr_setstacksize (&attr, stacksize);
Miscellaneous Routines
pthread_self()
pthread_equal(thread1, thread2)
pthread_self returns the unique, system assigned thread ID of the calling thread.
pthread_equal compares two thread IDs. If the two IDs are different 0 is returned,
otherwise a non-zero value is returned.
Note that for both of these routines, the thread identifier objects are opaque and
can not be easily inspected. Because thread IDs are opaque objects, the C language
equivalence operator (==) should not be used to compare two thread IDs against
each other, or to compare a single thread ID against another value.
pthread_once(once_control, init_routine)
pthread_once executes the init_routine exactly once in a process. The first call to this
routine by any thread in the process executes the given init_routine, without
parameters. Any subsequent calls will have no effect.
The init_routine routine is typically an initialization routine.
The once_control parameter is a synchronization control structure that requires
initialization prior to calling pthread_once. For example:
pthread_once_t once_control = PTHREAD_ONCE_INIT;`
Page 27 of 50
6 Exercise 1
Overview:
Login to an LC cluster using your workshop username and OTP token
Copy the exercise files to your home directory
Familiarize yourself with LC’s Pthreads environment
Write a simple “Hello World” Pthreads program
Successfully compile your program
Successfully run your program - several different ways
Review, compile, run and/or debug some related Pthreads programs
(provided)
1. Login to the workshop machine
Workshops differ in how this is done. The instructor will go over this beforehand.
2. Copy the example files
In your home directory, create a pthreads subdirectory for the example codes, copy
the example codes and then cd into your pthreads subdirectory:
mkdir pthreads
cp /usr/global/docs/training/blaise/pthreads/* ~/pthreads
cd pthreads
Page 28 of 50
3. List the contents of your pthreads subdirectory
You should notice a number of files as shown in the tables below.
File Name</span> Description</span>
Page 29 of 50
File Name</span> Description</span>
Page 30 of 50
o Main program calls pthread_exit as the last thing it does If you need
help, see the provided hello.c file.
Using your choice of compiler (see above section 4), compile your hello world
Pthreads program. This may take several attempts if there are any code
errors. For example:
icc -pthread -o hello myhello.c
pgcc -lpthread -o hello myhello.c
gcc -pthread -o hello myhello.c
Kur të merrni një përpilim të pastër, vazhdoni.
• Ekzekutoni ekzekutuesin tuaj hello dhe vini re daljen e tij. A është ajo që
prisnit? Si krahasim, mund të përpiloni dhe ekzekutoni programin shembull të
dhënë hello.c.
• Shënime:
o Për pjesën e mbetur të këtij ushtrimi, mund të përdorni komandën e
përpiluesit sipas zgjedhjes suaj, përveç nëse tregohet ndryshe.
o Përpiluesit do të ndryshojnë në cilat paralajmërime lëshojnë, por të gjithë
mund të injorohen për këtë ushtrim. Gabimet janë të ndryshme, natyrisht.
6. Planifikimi i Temave
• Rishikoni shembullin e kodit hello32.c. Vini re se do të krijojë 32 fije. Një
gjumë (); deklarata është prezantuar për të ndihmuar në sigurimin që të
gjitha temat do të ekzistojnë në të njëjtën kohë. Gjithashtu, çdo thread kryen
punë aktuale për të demonstruar se si sjellja e planifikuesit të OS përcakton
rendin e përfundimit të thread-it.
• Përpiloni dhe ekzekutoni programin. Vini re rendin në të cilin shfaqet
prodhimi i thread-it. A është ndonjëherë në të njëjtin rend? Si shpjegohet kjo?
7. Kalimi i argumenteve
• Rishikoni kodet shembull hello_arg1.c dhe hello_arg2.c. Vini re se si kalohet
argumenti i vetëm dhe si të kaloni argumente të shumta nëpër një strukturë.
• Përpiloni dhe ekzekutoni të dy programet dhe vëzhgoni daljen.
• Tani rishikoni, përpiloni dhe ekzekutoni programin bug3.c. Çfarë nuk shkon?
Si do ta rregullonit? Shihni shpjegimin në tabelën e programeve të gabimeve
më sipër.
8. Fillimi që del
Rishikoni, përpiloni (për gcc përfshin flamurin -lm) dhe ekzekutoni
programin bug5.c.
Çfarë ndodh? Pse? Si do ta rregullonit?
Shihni shpjegimin në tabelën e programeve të gabimeve më sipër.
9. Lidhja e fijeve
• Rishikoni, përpiloni (për gcc përfshin flamurin -lm) dhe ekzekutoni
programin join.c.
• Ndryshoni programin në mënyrë që thread-at të dërgojnë një kod
tjetër kthimi - ju zgjidhni. Përpiloni dhe ekzekutoni. A funksionoi?
• Për krahasim, rishikoni, përpiloni (për gcc përfshini flamurin -lm) dhe
ekzekutoni kodin shembull detached.c.
• Vëzhgoni sjelljen dhe vini re se nuk ka "bashkim" në këtë shembull.
10. Menaxhimi i Stackit
• Rishikoni, përpiloni dhe ekzekutoni programin bug2.c.
Page 31 of 50
• Çfarë ndodh? Pse? Si do ta rregullonit?
• Shihni shpjegimin në tabelën e programeve të gabimeve më sipër.
Rishikoni dhe provoni gjithashtu programin bug2fix.c.
Mutex Variables
Mutex Variables Overview
Mutex është një shkurtim për "përjashtim reciprok". Variablat Mutex janë një nga
mjetet kryesore për zbatimin e sinkronizimit të thread-it dhe për mbrojtjen e të
dhënave të përbashkëta kur ndodhin shkrime të shumta.
Një variabël mutex vepron si një "bllokim" që mbron aksesin në një burim të
përbashkët të të dhënave. Koncepti bazë i një mutex siç përdoret në Pthreads është
se vetëm një thread mund të bllokojë (ose të zotërojë) një ndryshore mutex në çdo
kohë të caktuar. Kështu, edhe nëse disa thread përpiqen të bllokojnë një mutex
vetëm një thread do të jetë i suksesshëm. Asnjë thread tjetër nuk mund ta zotërojë
atë mutex derisa fillesa zotëruese ta zhbllokojë atë mutex. Temat duhet të "marrin
radhë" për të hyrë në të dhënat e mbrojtura.
Mutexes mund të përdoren për të parandaluar kushtet e "racës". Një shembull i një
kushti gare që përfshin një transaksion bankar është paraqitur më poshtë:
Page 32 of 50
Në shembullin e mësipërm, një mutex duhet të përdoret për të kyçur "Bilancin"
ndërsa një thread po përdor këtë burim të përbashkët të të dhënave.
Mutex zakonisht përdoren kur përditësohen variablat globale. Kjo është një mënyrë
e sigurt për të siguruar që kur disa thread përditësojnë të njëjtën ndryshore, vlera
përfundimtare është e njëjtë me atë që do të ishte nëse vetëm një thread do të
kryente përditësimin. Variablat që përditësohen i përkasin një seksioni kritik.
Një sekuencë tipike në përdorimin e një mutex është si më poshtë:
• Krijoni dhe inicializoni një ndryshore mutex
• Disa fije tentojnë të bllokojnë mutex-in
• Vetëm një ka sukses dhe ai fije zotëron mutex
• Fillimi i pronarit kryen disa grupe veprimesh
• Pronari zhbllokon mutex-in
• Një fije tjetër fiton mutex dhe përsërit procesin
• Më në fund mutex shkatërrohet
Kur disa threads konkurrojnë për një mutex, humbësit bllokojnë në atë thirrje - një
thirrje pa bllokim është e disponueshme me "trylock" në vend të thirrjes "lock".
Kur mbroni të dhënat e përbashkëta, është përgjegjësia e programuesit që të
sigurohet që çdo thread që duhet të përdorë një mutex e bën këtë. Për shembull,
nëse 4 threads po përditësojnë të njëjtat të dhëna, por vetëm një përdor një mutex,
të dhënat ende mund të korruptohen
Page 33 of 50
pthread_mutex_destroy() duhet të përdoret për të liruar një objekt mutex i cili nuk
është më i nevojshëm.
Question: Kur më shumë se një thread është duke pritur për një mutex të kyçur,
cilës fillesë do t'i jepet kyçja e parë pasi të lëshohet?
Page 34 of 50
Example: Using Mutexes
• Ky shembull program ilustron përdorimin e variablave mutex në një
program Pthreads që kryen një produkt me pika.
• Të dhënat kryesore vihen në dispozicion për të gjitha thread-et nëpërmjet
një strukture të aksesueshme globalisht.
• Çdo thread punon në një pjesë të ndryshme të të dhënave.
• Fillimi kryesor pret që të gjitha threadët të përfundojnë llogaritjet e tyre dhe
më pas printon shumën që rezulton.
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
typedef struct
{
double *a;
double *b;
double sum;
int veclen;
} DOTDATA;
#define NUMTHRDS 4
#define VECLEN 100
DOTDATA dotstr;
pthread_t callThd[NUMTHRDS];
pthread_mutex_t mutexsum;
Page 35 of 50
/* Define and use local variables for convenience */
len = [Link];
start = offset * len;
end = start + len;
x = dotstr.a;
y = dotstr.b;
/*
Perform the dot product and assign result
to the appropriate variable in the structure.
*/
mysum = 0;
for (i = start; i < end ; i++) {
mysum += (x[i] * y[i]);
}
/*
Lock a mutex prior to updating the value in the shared
structure, and unlock it upon updating.
*/
pthread_mutex_lock(&mutexsum);
[Link] += mysum;
pthread_mutex_unlock(&mutexsum);
pthread_exit((void*) 0);
}
/* The main program creates threads which do all the work and then
* print out result upon completion. Before creating the threads,
* the input data is created. Since all threads update a shared structure,
* we need a mutex for mutual exclusion. The main thread needs to wait for
* all threads to complete, it waits for each one of the threads. We specify
* a thread attribute value that allow the main thread to join with the
* threads it creates. Note also that we free up handles when they are
* no longer needed.
*/
Page 36 of 50
pthread_attr_t attr;
[Link] = VECLEN;
dotstr.a = a;
dotstr.b = b;
[Link] = 0;
pthread_mutex_init(&mutexsum, NULL);
pthread_attr_destroy(&attr);
Page 37 of 50
Condition Variables
Condition Variables Overview
Variablat e gjendjes ofrojnë një mënyrë tjetër për sinkronizimin e thread-eve. Ndërsa mutexes
zbatojnë sinkronizimin duke kontrolluar aksesin e thread-it në të dhëna, variablat e kushteve
lejojnë që thread-et të sinkronizohen bazuar në vlerën aktuale të të dhënave.
Pa variablat e kushteve, programuesit do t'i duhej të kishte thread-e që po vrojtojnë vazhdimisht
(ndoshta në një seksion kritik), për të kontrolluar nëse kushti plotësohet. Kjo mund të konsumojë
shumë burime pasi që filli do të ishte vazhdimisht i zënë në këtë aktivitet. Një variabël kushti
është një mënyrë për të arritur të njëjtin qëllim pa sondazh.
Një variabël kushti përdoret gjithmonë në lidhje me një bllokues mutex.
Një sekuencë përfaqësuese për përdorimin e variablave të kushteve është paraqitur më poshtë.
Main Thread
Declare and initialize global data/variables which require
synchronization (such as "count")
Declare and initialize a condition variable object
Declare and initialize an associated mutex
Create threads A and B to do work
Thread A Thread B
Do work up to the point where a certain condition must occur (such Do work
Page 38 of 50
Main Thread
Declare and initialize global data/variables which require
synchronization (such as "count")
Declare and initialize a condition variable object
Declare and initialize an associated mutex
Create threads A and B to do work
Main Thread
Join / Continue
Page 40 of 50
Recommendation: Using a WHILE loop instead of an IF statement (see
watch_count routine in example below) to check the waited for condition can
help deal with several potential problems, such as:
o If several threads are waiting for the same wake up signal, they will
take turns acquiring the mutex, and any one of them can then modify
the condition they all waited for.
o If the thread received the signal in error due to a program bug
o The Pthreads library is permitted to issue spurious wake ups to a
waiting thread without violating the standard.
The pthread_cond_signal() routine is used to signal (or wake up) another
thread which is waiting on the condition variable. It should be called after
mutex is locked, and must unlock mutex in order
for pthread_cond_wait() routine to complete.
The pthread_cond_broadcast() routine should be used instead
of pthread_cond_signal() if more than one thread is in a blocking wait state.
It is a logical error to call pthread_cond_signal() before
calling pthread_cond_wait().
Proper locking and unlocking of the associated mutex variable is essential
when using these routines. For example:
o Failing to lock the mutex before calling pthread_cond_wait() may cause
it NOT to block.
o Failing to unlock the mutex after calling pthread_cond_signal() may not
allow a matching pthread_cond_wait() routine to complete (it will remain
blocked).
#define NUM_THREADS 3
#define TCOUNT 10
#define COUNT_LIMIT 12
Page 41 of 50
int count = 0;
pthread_mutex_t count_mutex;
pthread_cond_t count_threshold_cv;
/* Check the value of count and signal waiting thread when condition is
* reached. Note that this occurs while mutex is locked.
*/
if (count == COUNT_LIMIT) {
printf("inc_count(): thread %ld, count = %d -- threshold reached.",
my_id, count);
pthread_cond_signal(&count_threshold_cv);
printf("Just sent signal.\n");
}
printf("inc_count(): thread %ld, count = %d -- unlocking mutex\n",
my_id, count);
pthread_mutex_unlock(&count_mutex);
/* Lock mutex and wait for signal. Note that the pthread_cond_wait routine
* will automatically and atomically unlock mutex while it waits.
* Also, note that if COUNT_LIMIT is reached before this routine is run by
* the waiting thread, the loop will be skipped to prevent pthread_cond_wait
* from never returning.
*/
pthread_mutex_lock(&count_mutex);
while (count < COUNT_LIMIT) {
printf("watch_count(): thread %ld Count= %d. Going into wait...\n", my_id,count);
pthread_cond_wait(&count_threshold_cv, &count_mutex);
Page 42 of 50
printf("watch_count(): thread %ld Condition signal received. Count= %d\n",
my_id,count);
}
printf("watch_count(): thread %ld Updating the value of count...\n", my_id);
count += 125;
printf("watch_count(): thread %ld count now = %d.\n", my_id, count);
printf("watch_count(): thread %ld Unlocking mutex.\n", my_id);
pthread_mutex_unlock(&count_mutex);
pthread_exit(NULL);
}
}
Source
Output
Page 43 of 50
Monitoring, Debugging, and Performance
Analysis for Pthreads
Monitoring and Debugging Pthreads
• Debugers ndryshojnë në aftësinë e tyre për të trajtuar Pthreads. Korrigjuesi TotalView është
korrigjuesi i rekomanduar i LC për programet paralele. Ai është i përshtatshëm si për
monitorimin ashtu edhe për korrigjimin e programeve të filetuara.
• Një shembull i pamjes së ekranit nga një sesion TotalView duke përdorur një kod të filetuar
është paraqitur më poshtë.
1. Paneli i gjurmimit të stivës: Shfaq grupin e thirrjeve të rutinave që po ekzekuton filli i
përzgjedhur.
2. Shiritat e statusit: Shfaq informacionin e statusit për fillin e zgjedhur dhe procesin e
lidhur me të.
3. Paneli i kornizës së stivës: Tregon variablat, regjistrat, etj.
4. Paneli i burimit: Tregon kodin burimor për fillin e zgjedhur.
5. Dritarja e rrënjës që tregon të gjitha temat
6. Paneli i Threads: Tregon threads të lidhur me procesin e zgjedhur
Page 44 of 50
Page 45 of 50
See the TotalView Debugger tutorial for details.
Komanda Linux ps ofron disa flamuj për shikimin e informacionit të temave. Disa
shembuj janë paraqitur më poshtë. Shikoni faqen e njeriut për detaje.
% ps -Lf
UID PID PPID LWP C NLWP STIME TTY TIME CMD
blaise 22529 28240 22529 0 5 11:31 pts/53 00:00:00 [Link]
blaise 22529 28240 22530 99 5 11:31 pts/53 00:01:24 [Link]
blaise 22529 28240 22531 99 5 11:31 pts/53 00:01:24 [Link]
blaise 22529 28240 22532 99 5 11:31 pts/53 00:01:24 [Link]
blaise 22529 28240 22533 99 5 11:31 pts/53 00:01:24 [Link]
% ps -T
PID SPID TTY TIME CMD
22529 22529 pts/53 00:00:00 [Link]
22529 22530 pts/53 00:01:49 [Link]
22529 22531 pts/53 00:01:49 [Link]
22529 22532 pts/53 00:01:49 [Link]
22529 22533 pts/53 00:01:49 [Link]
% ps -Lm
PID LWP TTY TIME CMD
22529 - pts/53 00:18:56 [Link]
- 22529 - 00:00:00 -
- 22530 - 00:04:44 -
- 22531 - 00:04:44 -
- 22532 - 00:04:44 -
- 22533 - 00:04:44 -
Page 46 of 50
Performance Analysis Tools:
• Ka një shumëllojshmëri mjetesh të analizës së performancës që mund të përdoren me
programet e filetuara. Kërkimi në ueb do të sjellë një mori informacionesh.
• Në LC, lista e mjeteve kompjuterike të mbështetura mund të gjendet
në: [Link]
• Këto mjete ndryshojnë ndjeshëm në kompleksitetin, funksionalitetin dhe kurbën e të
mësuarit. Mbulimi i tyre në detaje është përtej qëllimit të këtij tutoriali.
• Disa mjete që ia vlen të hetohen, veçanërisht për kodet e filetuara, përfshijnë:
o Open|SpeedShop
o TAU
o HPCToolkit
o PAPI
o Intel VTune Amplifier
o ThreadSpotter
Page 47 of 50
Implementations:
Të gjitha sistemet e prodhimit LC përfshijnë një implementim Pthreads që ndjek
draftin 10 (përfundimtar) të standardit POSIX. Ky është zbatimi i preferuar. Zbatimet
ndryshojnë në numrin maksimal të thread-eve që mund të krijojë një proces. Ato
ndryshojnë gjithashtu në sasinë e paracaktuar të hapësirës së stivës së fijeve.
Compiling:
LC mban një numër përpiluesish, dhe zakonisht disa versione të ndryshme të secilit
- shihni faqen e internetit të Kompiluesve të Mbështetur të LC. Komandat e
përpiluesit të përshkruara në seksionin Përpilimi i programeve të filetuara zbatohen
për sistemet LC.
Mixing MPI with Pthreads:
Ky është motivimi kryesor për përdorimin e Pthreads në LC.
Design:
Çdo proces MPI zakonisht krijon dhe më pas menaxhon N thread, ku N përdor më së
miri bërthamat/nyjet e disponueshme. Gjetja e vlerës më të mirë për N do të
ndryshojë me platformën dhe karakteristikat e aplikacionit tuaj. Në përgjithësi,
mund të ketë probleme nëse fijet e shumta bëjnë thirrje MPI. Programi mund të
dështojë ose të sillet në mënyrë të papritur. Nëse thirrjet MPI duhet të bëhen nga
brenda një thread, ato duhet të bëhen vetëm nga një thread.
Compiling:
Use the appropriate MPI compile command for the platform and language of choice
Be sure to include the required Pthreads flag as shown in the Compiling Threaded
Programs section. An example code that uses both MPI and Pthreads is available
below. The serial, threads-only, MPI-only and MPI-with-threads versions demonstrate
one possible progression.
Serial
Pthreads only
MPI only
MPI with pthreads
Makefile
Page 48 of 50
Keys: Thread-Specific Data
o As threads call and return from different routines, the local data on a
thread’s stack comes and goes.
o To preserve stack data you can usually pass it as an argument from
one routine to the next, or else store the data in a global variable
associated with a thread.
o Pthreads provides another, possibly more convenient and versatile,
way of accomplishing this through keys.
Mutex Protocol Attributes and Mutex Priority Management for the handling of
“priority inversion” problems.
o Condition Variable Sharing—across processes
o Thread Cancellation
o Threads and Signals
o Sychronization constructs—barriers and locks
Exercise 2
1. Mutexes
1. Review, compile and run the dotprod_serial.c program. As its name implies, it
is serial - no threads are created.
2. Now review, compile and run the dotprod_mutex.c program. This version of
the dotprod program uses threads and requires a mutex to protect the global
sum as each thread updates it with their partial sums.
3. Execute the dotprod_mutex program several times and notice that the order
in which threads update the global sum varies.
4. Review, compile and run the bug6.c program.
5. Run it several times and notice what the global sum is each time? See if you
can figure out why and fix it. The explanation is provided in the bug examples
table above, and an example solution is provided by the bug6fix.c program.
6. The arrayloops.c program is another example of using a mutex to protect
updates to a global sum. Feel free to review, compile and run this example
code as well.
2. Condition Variables
1. Rishikoni, përpiloni dhe ekzekutoni programin condvar.c. Ky shembull është
në thelb i njëjtë me atë të treguar në tutorial. Vëzhgoni daljen e tre fijeve.
2. Tani, rishikoni, përpiloni dhe ekzekutoni programin bug1.c. Vëzhgoni daljen
e pesë fijeve. Çfarë ndodh? Shihni nëse mund të përcaktoni pse dhe
rregulloni problemin. Shpjegimi është dhënë në tabelën e shembujve të
gabimeve të mësipërme dhe një zgjidhje shembulli ofrohet nga programi
bug1fix.c.
3. Programi bug4.c është një shembull tjetër i asaj që mund të shkojë keq kur
përdoren variablat e kushteve. Rishikoni, përpiloni (për gcc përfshini
flamurin -lm) dhe ekzekutoni kodin. Vëzhgoni rezultatin dhe më pas shikoni
nëse mund ta rregulloni problemin. Shpjegimi është dhënë në tabelën e
shembujve të gabimeve të mësipërme dhe një zgjidhje shembulli ofrohet
nga programi bug4fix.c.
Page 49 of 50
3. Hybrid MPI with Pthreads
1. Directoria pthreads e juaja duhet të përmbajë 5 kodet e mëposhtme y should
contain the following 5 codes:
mpithreads_serial.c
mpithreads_threads.c
mpithreads_mpi.c
mpithreads_both.c
[Link]
Këto kode zbatojnë një llogaritje të produktit me pika dhe janë krijuar për të treguar
përparimin e zhvillimit të një programi hibrid MPI / Pthreads nga një kod serial.
Madhësia e problemit rritet ndërsa shembujt shkojnë nga serial, në threads/mpi në
mpi me threads.
Sugjerim: thjesht krijimi dhe ekzekutimi i kësaj serie kodesh është mjaft i
pavëmendshëm. Përdorimi i kohës së disponueshme laboratorike për të kuptuar se
çfarë po ndodh në të vërtetë është qëllimi. Instruktori është në dispozicion për
pyetjet tuaja.
1. Review each of the codes. The order of the listing above shows the
“progression”.
2. Use the provided makefile to compile all of the codes at once. The makefile
uses the gcc compiler - feel free to modify it and use a different compiler.
3. make -f [Link]
4. Run each of the codes and observe their output:
Execution command Description
mpithreads_serial Serial version - no threads or MPI
srun -n8 -ppReservedMPI only version with 8 tasks running on a single node in the
mpithreads_mpi special workshop pool
MPI with threads using 4 tasks running on 4 different nodes,
srun -N4 -ppReserved
mpithreads_both
each of which spawns 8 threads, running in special workshop
pool
Page 50 of 50