0% found this document useful (0 votes)
4 views50 pages

POSIX Threads Programming AL

Uploaded by

ShpendIsmaili
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
4 views50 pages

POSIX Threads Programming AL

Uploaded by

ShpendIsmaili
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd

POSIX

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

Efficient Communications/Data Exchange:


Pthreads mund të përdoren për të arritur performancën optimale në një mjedis kompjuterik me
performancë të lartë. Në veçanti, nëse një aplikacion përdor MPI për komunikime në nyje,
ekziston mundësia që performanca të përmirësohet duke përdorur Pthreads në vend.
Bibliotekat MPI zakonisht zbatojnë komunikimin e detyrave në nyje nëpërmjet memories së
përbashkët, e cila përfshin të paktën një operacion kopjimi të memories (procesi për përpunim).

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.

Siguria e fijeve - Thread-safeness


Siguria e fijeve, me pak fjalë, i referohet aftësisë së një aplikacioni për të ekzekutuar thread-e të
shumta në të njëjtën kohë pa "grupuar" të dhënat e përbashkëta ose duke krijuar kushte gare.
Për shembull, supozoni se aplikacioni juaj krijon disa tema, secila prej të cilave bën thirrje për të
njëjtën rutinë të bibliotekës:
• Kjo rutinë bibliotekare akseson/modifikon një strukturë ose vendndodhje globale në
memorie.
• Ndërsa çdo thread e quan këtë rutinë, është e mundur që ata mund të përpiqen të
modifikojnë këtë strukturë globale/vendndodhje të memories në të njëjtën kohë.
• Nëse rutina nuk përdor një lloj konstruksioni sinkronizimi për të parandaluar
korrupsionin e të dhënave, atëherë nuk është e sigurt në fije.

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.

3 The Pthreads API


The Pthreads API
API origjinal i Pthreads u përcaktua në standardin ANSI/IEEE POSIX 1003.1 - 1995. Standardi
POSIX ka vazhduar të zhvillohet dhe t'i nënshtrohet rishikimeve, duke përfshirë specifikimin
Pthreads.
Kopjet e standardit mund të blihen nga IEEE ose të shkarkohen falas nga faqet e tjera në
internet.

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ë.

Routine Prefix Functional Group

pthread_ Threads themselves and miscellaneous subroutines

pthread_attr_ Thread attributes objects

pthread_mutex_ Mutexes

pthread_mutexattr_ Mutex attributes objects

pthread_cond_ Condition variables

pthread_condattr_ Condition attributes objects

pthread_key_ Thread-specific data keys

pthread_rwlock_ Read/write locks

pthread_barrier_ Synchronization barriers

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.

Compiling Threaded Programs


Compiling Threaded Programs
Disa shembuj të komandave të përdorura për të përpiluar kodin që përdor threads janë renditur
në tabelën më poshtë.
Compiler /
Compiler Command Description
Platform

INTEL icc -pthread C


Linux
icpc -pthread C++

PGI pgcc -lpthread C


Linux
pgCC -lpthread C++

GNU gcc -pthread GNU C


Linux, Blue Gene
g++ -pthread GNU C++

IBM bgxlc_r / bgcc_r C (ANSI / non-ANSI)


Blue Gene
bgxlC_r, bgxlc++_r C++

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 maxproc unlimited

% 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ë.

Thread Binding and Scheduling:


Pyetje: Pasi të jetë krijuar një thread, si e dini a) kur do të planifikohet të
ekzekutohet nga sistemi operativ dhe b) në cilin procesor/bërthamë do të
funksionojë?
Klikoni për përgjigje
API Pthreads ofron disa rutina që mund të përdoren për të specifikuar se si threads
janë planifikuar për ekzekutim. Për shembull, thread-et mund të planifikohen të
ekzekutojnë FIFO (first-in first-out), RR (round-robin) ose OTHER (sistemi operativ
përcakton). Ai gjithashtu ofron mundësinë për të vendosur vlerën e prioritetit të
planifikimit të një thread.
Këto tema nuk mbulohen këtu, megjithatë një pasqyrë e mirë e "si funksionojnë
gjërat" në Linux mund të gjendet në faqen e njeriut sched_setscheduler.
API-ja e Pthreads nuk ofron rutina për lidhjen e temave me cpus/bërthamë specifike.
Megjithatë, implementimet lokale mund të përfshijnë këtë funksionalitet - si [Link].
ofrimi i rutinës jo standarde pthread_setaffinity_np. Vini re se "_np" në emër
qëndron për "non-portable".
Gjithashtu, sistemi operativ lokal mund të ofrojë një mënyrë për ta bërë këtë. Për
shembull, Linux ofron rutinën sched_setaffinity.

Terminating Threads & pthread_exit()


Ka disa mënyra në të cilat një thread mund të përfundojë:
• Fillimi kthehet normalisht nga rutina e tij e fillimit. Puna e saj është kryer.
• Thread bën një thirrje në nënprogramin pthread_exit - nëse puna e tij është
kryer apo jo.
• Fillimi anulohet nga një thread tjetër nëpërmjet rutinës pthread_cancel.
• I gjithë procesi përfundon për shkak të kryerjes së një thirrjeje ose te exec()
ose te exit()
• Nëse main() përfundon së pari, pa thirrur vetë pthread_exit në mënyrë
eksplicite
Rutina pthread_exit() lejon programuesin të specifikojë një parametër opsional të
statusit të përfundimit. Ky parametër opsional zakonisht kthehet në thread-et që
"bashkohen" me thread-in e përfunduar (të mbuluar më vonë).
Në nënprogramet që ekzekutohen deri në përfundim normalisht, shpesh mund të
hiqni dorë nga thirrja e pthread_exit() - përveç nëse, sigurisht, dëshironi të merrni
vlerën e kthimit të rutinës.
Pastrimi: rutina pthread_exit() nuk mbyll skedarët; çdo skedar i hapur brenda
thread-it do të mbetet i hapur pas përfundimit të thread-it.
Diskutim mbi thirrjen e pthread_exit() nga main():
 • Ka një problem të caktuar nëse main() përfundon përpara thread-eve që ka
krijuar nëse nuk e thërrisni në mënyrë eksplicite pthread_exit(). Të gjitha
temat që krijoi do të përfundojnë sepse main() është kryer dhe nuk ekziston
më për të mbështetur thread-ët.

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

void *PrintHello(void *threadid)


{
long tid;
tid = (long)threadid;
printf("Hello World! It's me, thread #%ld!\n", tid);
pthread_exit(NULL);
}

int main (int argc, char *argv[])


{
pthread_t threads[NUM_THREADS];
int rc;
long t;
for(t = 0; t < NUM_THREADS; t++) {
printf("In main: creating thread %ld\n", t);
rc = pthread_create(&threads[t], NULL, PrintHello, (void *)t);
if (rc) {
printf("ERROR; return code from pthread_create() is %d\n", rc);
exit(-1);
}
}

/* Last thing that main() should do */


pthread_exit(NULL);
}

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!

Passing Arguments to Threads


Rutina pthread_create() i lejon programuesit të kalojë një argument në rutinën e
fillimit të thread-it. Për rastet kur duhet të kalohen disa argumente, ky kufizim
kapërcehet lehtësisht duke krijuar një strukturë që përmban të gjitha argumentet,
pastaj duke kaluar një tregues në atë strukturë në rutinën pthread_create().
Të gjitha argumentet duhet të kalohen me referencë dhe të hidhen në (void *).
Pyetje: Duke pasur parasysh fillimin dhe planifikimin e tyre jo-përcaktues, si mund
t'i kaloni të dhënat në mënyrë të sigurt te temat e krijuara rishtazi?
Përgjigja (Kliko për të parë.)*Sigurohuni që të gjitha të dhënat e kaluara të jenë të
sigurta në lidhje - që nuk mund të ndryshohen nga temat e tjera. Tre shembujt që
vijojnë tregojnë se çfarë duhet dhe që nuk duhet të bësh.*
Shembulli 1 - Kalimi i argumentit të fillit
Ky fragment kodi tregon se si të kalohet një numër i plotë i thjeshtë në çdo thread.
Fillimi thirrës përdor një strukturë unike të dhënash për çdo thread, duke siguruar
që argumenti i çdo thread të mbetet i paprekur gjatë gjithë programit.

long taskids[NUM_THREADS];

for(t = 0; t < NUM_THREADS; t++)


{
taskids[t] = t;
printf("Creating thread %ld\n", t);
rc = pthread_create(&threads[t], NULL, PrintHello, (void *) taskids[t]);
/* ... */
}

See the source code.


Output:
Creating thread 0

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;
};

struct thread_data thread_data_array[NUM_THREADS];

void *PrintHello(void *threadarg)


{
struct thread_data *my_data;
...
my_data = (struct thread_data *) threadarg;
taskid = my_data->thread_id;
sum = my_data->sum;
hello_msg = my_data->message;
...
}

int main (int argc, char *argv[])


{
...
thread_data_array[t].thread_id = t;
thread_data_array[t].sum = sum;
thread_data_array[t].message = messages[t];
rc = pthread_create(&threads[t], NULL, PrintHello,
(void *) &thread_data_array[t]);
...
}

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;

for(t=0; t<NUM_THREADS; t++)


{
printf("Creating thread %ld\n", t);
rc = pthread_create(&threads[t], NULL, PrintHello, (void *) &t);
...
}

See the source code.


Output:

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

Joining and Detaching Threads


Routines:
pthread_join(thread, status)
pthread_detach(thread)
pthread_attr_setdetachstate(attr, detachstate)
pthread_attr_getdetachstate(attr)
Joining:
“Joining” is one way to accomplish synchronization between threads. For example:

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ë:

 Declare a pthread attribute variable of the pthread_attr_t data type


 Initialize the attribute variable with pthread_attr_init()
 Set the attribute detached status with pthread_attr_setdetachstate()
 When done, free library resources used by the attribute with pthread_attr_destroy()

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.

Pthreads Joining Example


#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#define NUM_THREADS 4

void *BusyWork(void *t)


{
int i;
long tid;
double result = 0.0;
tid = (long)t;
printf("Thread %ld starting...\n",tid);
for (i=0; i<1000000; i++)
{
result = result + sin(i) * tan(i);
}
printf("Thread %ld done. Result = %e\n", tid, result);
pthread_exit((void*) t);
}

int main (int argc, char *argv[])


{
pthread_t thread[NUM_THREADS];
pthread_attr_t attr;
int rc;
long t;
void *status;

Page 23 of 50
/* Initialize and set thread detached attribute */
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

for(t = 0; t < NUM_THREADS; t++) {


printf("Main: creating thread %ld\n", t);
rc = pthread_create(&thread[t], &attr, BusyWork, (void *)t);
if (rc) {
printf("ERROR; return code from pthread_create() is %d\n", rc);
exit(-1);
}
}

/* Free attribute and wait for the other threads */


pthread_attr_destroy(&attr);
for(t=0; t<NUM_THREADS; t++) {
rc = pthread_join(thread[t], &status);
if (rc) {
printf("ERROR; return code from pthread_join() is %d\n", rc);
exit(-1);
}
printf("Main: completed join with thread %ld having a status
of %ld\n",t,(long)status);
}

printf("Main: program completed. Exiting.\n");


pthread_exit(NULL);
}

See the source code here.


Output:
Main: creating thread 0
Main: creating thread 1
Thread 0 starting...
Main: creating thread 2
Thread 1 starting...
Main: creating thread 3
Thread 2 starting...
Thread 3 starting...
Thread 1 done. Result = -3.153838e+06
Thread 0 done. Result = -3.153838e+06
Main: completed join with thread 0 having a status of 0
Main: completed join with thread 1 having a status of 1
Thread 3 done. Result = -3.153838e+06
Thread 2 done. Result = -3.153838e+06
Main: completed join with thread 2 having a status of 2
Main: completed join with thread 3 having a status of 3
Main: program completed. Exiting.

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)

Intel Xeon E5- 16 32 2,097,152


2670

Intel Xeon 5660 12 24 2,097,152

AMD Opteron 8 16 2,097,152

Intel IA64 4 8 33,554,432

Intel IA32 2 4 2,097,152

Page 25 of 50
Node #CPUs Memory (GB) Default Size
Architecture (bytes)

IBM Power5 8 32 196,608

IBM Power4 8 16 196,608

IBM Power3 16 16 98,304

Example: Stack Management


This example demonstrates how to query and set a thread’s stack size.
#include <pthread.h>
#include <stdio.h>

#define NTHREADS 4
#define N 1000
#define MEGEXTRA 1000000

pthread_attr_t attr;

void *dowork(void *threadid)


{
double A[N][N];
int i, j;
long tid;
size_t mystacksize;

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);
}

int main(int argc, char *argv[])


{
pthread_t threads[NTHREADS];
size_t stacksize;
int rc;
long t;

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);

printf("Creating threads with stack size = %li bytes\n", stacksize);


for(t=0; t<NTHREADS; t++){
rc = pthread_create(&threads[t], &attr, dowork, (void *)t);
if (rc){
printf("ERROR; return code from pthread_create() is %d\n", rc);
exit(-1);
}
}
printf("Created %ld threads.\n", t);
pthread_exit(NULL);
}

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>

arrayloops.c Data decomposition by loop distribution. Fortran example only


arrayloops.f works under IBM AIX: see comments in source code for
compilation instructions.

condvar.c Condition variable example file. Similar to what was shown in


the tutorial

detached.c Demonstrates how to explicitly create pthreads in a detached


state.

dotprod_mutex.c Mutex variable example using a dot product program. Both a


dotprod_serial.c serial and pthreads version of the code are available.

hello.c Simple "Hello World" example

hello32.c "Hello World" pthreads program demonstrating thread


scheduling behavior.

hello_arg1.c One correct way of passing the pthread_create() argument.

Page 29 of 50
File Name</span> Description</span>

hello_arg2.c Another correct method of passing the pthread_create()


argument, this time using a structure to pass multiple
arguments.

join.c Demonstrates how to explicitly create pthreads in a joinable


state for portability purposes. Also shows how to use the
pthread_exit status parameter.

mpithreads_serial.c A "series" of programs which demonstrate the progression for a


mpithreads_threads. serial dot product code to a hybrid MPI/pthreads
c implementation. Files include the serial version, pthreads
mpithreads_mpi.c version, MPI version, hybrid version and a makefile.
mpithreads_both.c
[Link]

4. Compilers - What’s Available?


 Visit the Compilers Currently Installed on LC Platforms webpage.
 Click on the name of the workshop Linux cluster (sierra) in the summary table
near the top of the page. This will take you to a table of available compilers.
 You can also view the available compilers in the Compilers section of the
Linux Clusters Overview tutorial.
 Now, in your cluster login window, try the use -l compilers command to display
available compilers. You should see GNU, Intel and PGI compilers - several
versions of each.
o Question: Which version is the default version?
o Answer: Use the dpkg-defaults command and look for the asterisk.
5. Create, compile and run a Pthreads “Hello world” program
 Using your favorite text editor (vi/vim, emacs, nedit, gedit, nano…) open a
new file - call it whatever you’d like.
 Create a simple Pthreads program that does the following:
o Includes the pthread.h header file
o Main program creates several threads, each of which executes a “print
hello” thread routine. The argument passed to that routine is their
thread ID.
o The thread’s “print hello” routine accepts the thread ID argument and
prints “hello world from thread #”. Then it calls pthread_exit to finish.

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ë:

Thread 1 Thread 2 Balance

Read balance: $1000 $1000

Read balance: $1000 $1000

Deposit $200 $1000

Deposit $200 $1000

Update balance $1000+$200 $1200

Update balance $1000+ $1200


$200

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

Creating and Destroying Mutexes


Routines:
pthread_mutex_init(mutex, attr)
pthread_mutex_destroy(mutex)
pthread_mutexattr_init(attr)
pthread_mutexattr_destroy(attr)
Usage:
Variablat Mutex duhet të deklarohen me llojin pthread_mutex_t dhe duhet të
inicializohen përpara se të mund të përdoren. Ekzistojnë dy mënyra për të
inicializuar një variabël mutex:
1. 1. Statikisht, kur deklarohet. Për shembull:
pthread_mutex_t mymutex = PTHREAD_MUTEX_INITIALIZER;
2. 2. Dinamikisht, duke përdorur pthread_mutex_init(). Për shembull:
3. pthread_mutex_t mymutex;
4. pthread_mutex_init(&mymutex, NULL);
Muteksi fillimisht është i zhbllokuar.
Objekti attr përdoret për të vendosur vetitë për objektin mutex dhe duhet të jetë i
tipit pthread_mutexattr_t nëse përdoret (mund të specifikohet si NULL për të
pranuar parazgjedhjet). Standardi Pthreads përcakton tre atribute opsionale mutex:
• Protokolli: Përcakton protokollin e përdorur për të parandaluar përmbysjet
prioritare për një mutex.
• Prioceiling: Përcakton tavanin e prioritetit të një mutex.
• Process-shared: Specifikon ndarjen e procesit të një mutex.
Vini re se jo të gjitha implementimet mund të ofrojnë tre atributet opsionale mutex.
Rutinat pthread_mutexattr_init() dhe pthread_mutexattr_destroy() përdoren për të
krijuar dhe shkatërruar respektivisht objektet e atributeve mutex.

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.

Locking and Unlocking Mutexes


Routines:
pthread_mutex_lock(mutex)
pthread_mutex_trylock(mutex)
pthread_mutex_unlock(mutex)
Usage:
Rutina pthread_mutex_lock() përdoret nga një thread për të marrë një lock në
variablin e specifikuar mutex. Nëse mutex është tashmë i kyçur nga një
bashkëbisedim tjetër, kjo thirrje do të bllokojë lidhjen thirrëse derisa mutex të
shkyçet.
pthread_mutex_trylock() do të përpiqet të bllokojë një mutex. Megjithatë, nëse
mutex është tashmë i kyçur, rutina do të kthehet menjëherë me një kod gabimi "të
zënë". Kjo rutinë mund të jetë e dobishme në parandalimin e kushteve të bllokimit,
si në një situatë të përmbysjes së përparësisë.
pthread_mutex_unlock() do të zhbllokojë një mutex nëse thirret nga thread-i
zotërues. Thirrja e kësaj rutine kërkohet pasi një thread të ketë përfunduar
përdorimin e të dhënave të mbrojtura nëse thread-ët e tjerë do të marrin mutex për
punën e tyre me të dhënat e mbrojtura. Një gabim do të kthehet nëse:
• Nëse mutex ishte tashmë i shkyçur
• Nëse mutex është në pronësi të një thread tjetër
Nuk ka asgjë "magjike" në lidhje me mutexes ... në fakt ato janë të ngjashme me
një "marrëveshje zotërinjsh" midis temave pjesëmarrëse. I takon programuesit të
sigurojë që të gjitha thread-et të bëjnë mutexes të kyçur dhe zhbllokuar në mënyrë
të përshtatshme. Skenari i mëposhtëm tregon një gabim logjik:
Thread 1 Thread 2 Thread 3
Lock Lock
A = 2 A = A+1 A = A*B
Unlock Unlock

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>

/* The following structure contains the necessary information


* to allow the function "dotprod" to access its input data and
* place its output into the structure.
*/

typedef struct
{
double *a;
double *b;
double sum;
int veclen;
} DOTDATA;

/* Define globally accessible variables and a mutex */

#define NUMTHRDS 4
#define VECLEN 100

DOTDATA dotstr;
pthread_t callThd[NUMTHRDS];
pthread_mutex_t mutexsum;

/* The function dotprod is activated when the thread is created.


* All input to this routine is obtained from a structure
* of type DOTDATA and all output from this function is written into
* this structure. The benefit of this approach is apparent for the
* multi-threaded program: when a thread is created we pass a single
* argument to the activated function - typically this argument
* is a thread number. All the other information required by the
* function is accessed from the globally accessible structure.
*/

void *dotprod(void *arg)


{

Page 35 of 50
/* Define and use local variables for convenience */

int i, start, end, len;


long offset;
double mysum, *x, *y;
offset = (long)arg;

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.
*/

int main (int argc, char *argv[])


{
long i;
double *a, *b;
void *status;

Page 36 of 50
pthread_attr_t attr;

/* Assign storage and initialize values */


a = (double*) malloc (NUMTHRDS * VECLEN * sizeof(double));
b = (double*) malloc (NUMTHRDS * VECLEN * sizeof(double));

for (i = 0; i < VECLEN * NUMTHRDS; i++) {


a[i] = 1.0;
b[i] = a[i];
}

[Link] = VECLEN;
dotstr.a = a;
dotstr.b = b;
[Link] = 0;

pthread_mutex_init(&mutexsum, NULL);

/* Create threads to perform the dot product */


pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

for(i = 0; i < NUMTHRDS; i++) {


/* Each thread works on a different set of data. The offset is specified
* by 'i'. The size of the data for each thread is indicated by VECLEN.
*/
pthread_create(&callThd[i], &attr, dotprod, (void *)i);
}

pthread_attr_destroy(&attr);

/* Wait on the other threads */


for(i = 0; i < NUMTHRDS; i++) {
pthread_join(callThd[i], &status);
}

/* After joining, print out the results and cleanup */


printf("Sum = %f\n", [Link]);
free(a);
free(b);
pthread_mutex_destroy(&mutexsum);
pthread_exit(NULL);
}
Serial version: source
Parallel version: source

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

as "count" must reach a specified value) Lock associated mutex


Lock associated mutex and check value of a global variable Change the value of the
Call pthread_cond_wait() to perform a blocking wait for signal global variable that
from Thread-B. Note that a call Thread-A is waiting
to pthread_cond_wait()automatically and atomically unlocks the upon.
associated mutex variable so that it can be used by Thread-B. Check value of the
When signalled, wake up. Mutex is automatically and atomically global Thread-A wait
locked. variable. If it fulfills the
Explicitly unlock mutex desired condition, signal
Continue Thread-A.
Unlock mutex.
Continue

Main Thread
Join / Continue

Creating and Destroying Condition


Variables
Routines:
pthread_cond_init(condition, attr)
pthread_cond_destroy(condition)
pthread_condattr_init(attr)
pthread_condattr_destroy(attr)
Usage:
Condition variables must be declared with type pthread_cond_t, and must be
initialized before they can be used. There are two ways to initialize a condition
variable:
1. Statically, when it is declared. For example: pthread_cond_t myconvar =
PTHREAD_COND_INITIALIZER;
2. Dynamically, with the pthread_cond_init() routine.
Page 39 of 50
The ID of the created condition variable is returned to the calling thread
through the condition parameter. This method permits setting condition
variable object attributes (attr).
The optional attr object is used to set condition variable attributes. There is only
one attribute defined for condition variables: process-shared, which allows the
condition variable to be seen by threads in other processes. The attribute object, if
used, must be of type pthread_condattr_t (may be specified as NULL to accept
defaults). Note that not all implementations may provide the process-shared
attribute.
The pthread_condattr_init() and pthread_condattr_destroy() routines are used to create
and destroy condition variable attribute objects.
pthread_cond_destroy() should be used to free a condition variable that is no longer
needed.

Waiting and Signaling on Condition


Variables
Routines:
pthread_cond_wait(condition,mutex)
pthread_cond_signal(condition)
pthread_cond_broadcast(condition)
Usage:
 pthread_cond_wait() blocks the calling thread until the specified condition is
signalled. This routine should be called while mutex is locked, and it will
automatically release the mutex while it waits. After signal is received and
thread is awakened, mutex will be automatically locked for use by the thread.
The programmer is then responsible for unlocking mutex when the thread is
finished with it.

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).

Example: Using Condition


Variables
 • Ky kod i thjeshtë shembull demonstron përdorimin e disa rutinave të
variablave të kushteve Pthread.
 • Rutina kryesore krijon tre fije.
 Two of the threads perform work and update a “count” variable.
 Fillimi i tretë pret derisa ndryshorja e numërimit të arrijë një vlerë të caktuar.
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>

#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;

void *inc_count(void *t)


{
int i;
long my_id = (long)t;

for (i = 0; i < TCOUNT; i++) {


pthread_mutex_lock(&count_mutex);
count++;

/* 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);

/* Do some work so threads can alternate on mutex lock */


sleep(1);
}
pthread_exit(NULL);
}

void *watch_count(void *t)


{
long my_id = (long)t;

printf("Starting watch_count(): thread %ld\n", my_id);

/* 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);
}

int main(int argc, char *argv[])


{
int i, rc;
long t1 = 1,
t2 = 2,
t3 = 3;
pthread_t threads[3];
pthread_attr_t attr;

/* Initialize mutex and condition variable objects */


pthread_mutex_init(&count_mutex, NULL);
pthread_cond_init (&count_threshold_cv, NULL);

/* For portability, explicitly create threads in a joinable state */


pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
pthread_create(&threads[0], &attr, watch_count, (void *)t1);
pthread_create(&threads[1], &attr, inc_count, (void *)t2);
pthread_create(&threads[2], &attr, inc_count, (void *)t3);

/* Wait for all threads to complete */


for (i = 0; i < NUM_THREADS; i++) {
pthread_join(threads[i], NULL);
}
printf ("Main(): Waited and joined with %d threads. Final value of count = %d.
Done.\n",
NUM_THREADS, count);

/* Clean up and exit */


pthread_attr_destroy(&attr);
pthread_mutex_destroy(&count_mutex);
pthread_cond_destroy(&count_threshold_cv);
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 -

Grupet Linux të LC ofrojnë gjithashtu komandën kryesore për të monitoruar


proceset në një nyje. Nëse përdoret me flamurin -H, fijet e përfshira në një proces
do të jenë të dukshme. Një shembull i komandës top -H është paraqitur më poshtë.
Procesi mëmë është PID 18010 i cili krijoi tre fije, të paraqitura si PID 18012,
18013 dhe 18014.

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

LLNL Specific Information and


Recommendations
Ky seksion përshkruan detaje specifike për sistemet e Livermore Computing.

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

Topics Not Covered


Several features of the Pthreads API are not covered in this tutorial. These are listed
below. See the Pthread Library Routines Reference section for more information.
 Thread Scheduling
o Implementations will differ on how threads are scheduled to run. In
most cases, the default mechanism is adequate.
o The Pthreads API provides routines to explicitly set thread scheduling
policies and priorities which may override the default mechanisms.
o The API does not require implementations to support these features.

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

mpithreads_threads Threads only version of the code using 8 threads

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

References and More Information


Original Author: Blaise Barney; Contact: hpc-tutorials@[Link], Livermore Computing.
"Pthreads Programming". B. Nichols et al. O'Reilly and Associates.
"Threads Primer". B. Lewis and D. Berg. Prentice Hall
"Programming With POSIX Threads". D. Butenhof. Addison Wesley
"Programming With Threads". S. Kleiman et al. Prentice Hall

Page 50 of 50

You might also like