Вы находитесь на странице: 1из 18

Practical Guide To Pthread Programming in C++

By Swaminathan Bhaskar
10/22/2008
Terminology:
* Process : A process is an instance of a program executable (ex: vi) that is identified as a unique entity
in the operating environment (ex: linux). It has its own address space and system state information (ex:
handles to system resources). or two processes to communicate with one another! they need to use
some form of Inter "rocess #ommunication (I"#) mechanism (ex: pipes! soc$ets! shared memory).
* Thread : A thread is a path of execution that is identified by a unique entity within a process. A
process can have multiple threads of execution and they all share the same process address space and
system state information. %ince all the threads within a process share the same address space! they can
communicate with each other directly using program ob&ects.
* Mutex : A mutex (short form for mutual exclusion loc$) is a programming construct that is used to
control access to a shared resource by multiple threads within a process.
* Condition Variable : A condition variable is a notification mechanism by which one or more threads
may be alerted of a condition they are interested in.
Prerequisites:
* 'inux (.).x $ernel
* *+, #-- compiler . g--
Pthread Fundamentals:
/o use "osix thread (pthread)! we need to include the following header:
#include <pthread.h>
/o successfully compile the #-- code with "osix thread (pthread)! we need to lin$ in the "osix thread
(pthread) library by specifying the following library option to g--:
-lpthread
/o create a "osix thread (pthread)! we use the following function:
int pthread_create(
pthread_t *id,
const pthread_attr_t *attr,
void *(*exec)(void*),
void *arg
);
/he arguments of pthread_create are as follows:
id Input pointer that will contain the thread identifier on successful return from the function
attr Input pointer to a structure that provides additional parameters for creating a custom
thread. he default is an un!ound" non#detached thread with a stack si$e of 8% and default
priority
exec Input pointer to a glo!al function that is to !e e&ecuted in the thread of e&ecution
arg Input pointer to the argument that is to !e passed to the function to !e e&ecuted !y the
thread
/he default non.detached thread is allocated storage by the system that needs to be released on
termination. /o wait for a non.detached thread to terminate and reclaim the allocated storage and get
the termination status! we use the following function:
int pthread_join(
pthread_t id,
void **status
);
/he arguments of pthread_join are as follows:
id Input that specifies the hread Id
status Input pointer to a pointer that on successful return will contain the termination status of
the specified thread
/o identify threads within a process! each thread is assigned a unique identifier. /his is also $now as
the /hread Id. /o get the identifier of a thread! use the following function:
pthread_t pthread_self(void);
0aving seen the basic functions in the pthread library! now we are ready to see some action. /he
following is a simple 0ello 1orld style example:
* !ello.cpp *
#include <iostrea">
#include <pthread.h>
using na"espace std;
#ode path $e $ant the thread to execute
void *$elco"e(void *arg) %
cout << &'d( & << pthread_self() << endl;
cout << &)elco"e to *threads *rogra""ing& << endl;
return (void *)+;
,
----- -ain -----
int "ain() %
int ret;
int *stat;
pthread_t tid;
#reate a thread $ithin the process to execute $elco"e
if ((ret . pthread_create(/tid, 0122, $elco"e, 0122)) 3. +) %
cout << &4rror creating thread( & << strerror(ret) << endl;
exit(5);
,
cout << &#reated 6hread & << tid << endl;
pthread_join(tid, (void **)/stat);
cout << &6hread & << tid << & ter"inated, 7tatus . & << stat << endl;
exit(+);
,
1e will save the above #-- code in a file called 0ello.cpp. /o compile the above #-- program!
execute the following command in the shell:
8 g99 -o !ello !ello.cpp -lpthread
2unning the executable Hello will create a thread that executes the function welcome.
Along with the included source! is a basic ma$e file 3a$efile! which will allow us to compile the
provided code. or example! to compile all the code! execute the following command in the shell:
8 "a:e
/o compile &ust Hello.cpp! execute the following command in the shell:
8 "a:e !ello
"osix thread (pthread) is a # library. In order to use it in an ob&ect.oriented language li$e #--! we will
create an ob&ect wrapper that will abstract and hide away the intricacies of "osix thread (pthread) and
present a simple interface for the user. /his abstraction will be encapsulated in the class named Thread
in all the following examples. /he following is the definition of the Thread class:
class 6hread %
private(
pthread_t _id;

*revent cop;ing or assign"ent
6hread(const 6hread/ arg);
6hread/ operator.(const 6hread/ rhs);

protected(
<ool started;
void *arg;

static void *exec(void *thr);

pu<lic(
6hread();
virtual =6hread();
unsigned int tid() const;
void start(void *arg . 0122);
void join();
virtual void run() . +;
,;
As indicated earlier! "osix thread is a # library and the function that creates a thread! pthread_create!
needs a pointer to a global function. In the Thread class! the static member function exec will serve as
the global function. /he Thread class is an abstract class because of the pure virtual function run. /he
member function run is internally called by the static member function exec.
/he following is a simple example that uses the abstract class Thread. /his example also shows! how
to pass arguments to a thread function:
* 6hread.cpp *
#include <iostrea">
#include <pthread.h>
using na"espace std;
----- #lass ( 6hread -----
class 6hread %
private(
pthread_t _id;

*revent cop;ing or assign"ent
6hread(const 6hread/ arg);
6hread/ operator.(const 6hread/ rhs);

protected(
void *arg;

static void *exec(void *thr);

pu<lic(
6hread();
virtual =6hread();
void start(void *arg);
void join();
virtual void run() . +;
,;
6hread((6hread() %
cout << &6hread((6hread()& << endl;
,
6hread((=6hread() %
cout << &6hread((=6hread()& << endl;
,
void 6hread((start(void *arg) %
int ret;
this->arg . arg;

*
* 7ince pthread_create is a # li<rar; function, the >rd argu"ent is
* a glo<al function that $ill <e executed <; the thread. 'n #99, $e
* e"ulate the glo<al function using the static "e"<er function that
* is called exec. 6he ?th argu"ent is the actual argu"ent passed to
* the function exec. !ere $e use this pointer, $hich is an instance
* of the 6hread class.
*
if ((ret . pthread_create(/_id, 0122, /6hread((exec, this)) 3. +) %
cout << strerror(ret) << endl;
thro$ &4rror&;
,
,
void 6hread((join() %
@llo$ the thread to $ait for the ter"ination status
pthread_join(_id, 0122);
,
Aunction that is to <e executed <; the thread
void *6hread((exec(void *thr) %
reinterpret_cast<6hread *> (thr)->run();
,
class -;6hread ( pu<lic 6hread %
pu<lic(
*
* 6his "ethod $ill <e executed <; the 6hread((exec "ethod,
* $hich is executed in the thread of execution
*
void run() %
cout << &)elco"e to *threads *rogra""ing in #99 $ith arg &
<< *((int *)arg) << endl;
,
,;
----- -ain -----
int "ain() %
int x . 5+;
-;6hread thr;
thr.start(/x);
thr.join();
,
/o create a non.default thread (ex: detached thread)! one has to provide the appropriate thread attributes
at the thread creation time. /hread attributes are encapsulated in a structure pthread_attr_t.
/he following example demonstrates the use of thread attributes to create a detached thread with a
stac$ si4e of PTHREAD_TAC!_M"# which is the minimum si4e to start a thread:
* @ttri<utes.cpp *
#include <iostrea">
#include <pthread.h>
using na"espace std;
----- #lass ( 6hread -----
class 6hread %
private(
pthread_t _id;
pthread_attr_t _attr;

*revent cop;ing or assign"ent
6hread(const 6hread/ arg);
6hread/ operator.(const 6hread/ rhs);

protected(
void *arg;

static void *exec(void *thr);

pu<lic(
6hread(<ool detach, int siBe);
virtual =6hread();
void start(void *arg);
void join();
virtual void run() . +;
,;
*
* 6he 5st argu"ent specifies $hether $e $ant to create a detached thread.
* 6he Cnd argu"ent specifies the stac: siBe of the thread
*
6hread((6hread(<ool detach, int siBe) %
cout << &6hread((6hread()& << endl;
int ret;
if ((ret . pthread_attr_init(/_attr)) 3. +) %
cout << strerror(ret) << endl;
thro$ &4rror&;
,
if (detach) %
if ((ret . pthread_attr_setdetachstate(/_attr,
*6!D4@E_#D4@64_E46@#!4E)) 3. +) %
cout << strerror(ret) << endl;
thro$ &4rror&;
,
,
if (siBe >. *6!D4@E_76@#F_-'0) %
if ((ret . pthread_attr_setstac:siBe(/_attr, siBe)) 3. +) %
cout << strerror(ret) << endl;
thro$ &4rror&;
,
,
,
6hread((=6hread() %
cout << &6hread((=6hread()& << endl;
int ret;
if ((ret . pthread_attr_destro;(/_attr)) 3. +) %
cout << strerror(ret) << endl;
thro$ &4rror&;
,
,
void 6hread((start(void *arg) %
int ret;
this->arg . arg;

*
* 7ince pthread_create is a # li<rar; function, the >rd argu"ent is
* a glo<al function that $ill <e executed <; the thread. 'n #99, $e
* e"ulate the glo<al function using the static "e"<er function that
* is called exec. 6he ?th argu"ent is the actual argu"ent passed to
* the function exec. !ere $e use this pointer, $hich is an instance
* of the 6hread class.
*
if ((ret . pthread_create(/_id, /_attr, /6hread((exec, this)) 3. +) %
cout << strerror(ret) << endl;
thro$ &4rror&;
,
,
void 6hread((join() %
@llo$ the thread to $ait for the ter"ination status
pthread_join(_id, 0122);
,
Aunction that is to <e executed <; the thread
void *6hread((exec(void *thr) %
reinterpret_cast<6hread *> (thr)->run();
,
class -;6hread ( pu<lic 6hread %
pu<lic(
-;6hread(<ool detach, int siBe) ( 6hread(detach, siBe) %,

*
* 6his "ethod $ill <e executed <; the 6hread((exec "ethod,
* $hich is executed in the thread of execution
*
void run() %
cout << &)elco"e to *threads )ith @ttri<utes in #99 $ith
arg & << *((int *)arg) << endl;
,
,;
----- -ain -----
int "ain() %
int x . 5+;
#reate a detached thread $ith "ini"u" stac: siBe
-;6hread thr(true, *6!D4@E_76@#F_-'0);
thr.start(/x);
,
Pthread Synchronization:
Mutex:
1hen multiple threads access and manipulate a shared resource (ex: a variable for instance)! the access
to the shared resource needs to be controlled through a loc$ mechanism so that only one thread is
allowed access to the shared resource at any point of time while the other threads are waiting to gain
access the shared resource. In "osix thread (pthread) this is enforced by using a synchroni4ation
primitive called mutex loc$.
A mutex loc$ ob&ect is encapsulated in a structure pthread_$utex_t. "rior to use! an instance of the
mutex loc$ ob&ect needs to be initiali4ed! which allocates storage for the internal attributes on the
ob&ect. /his is done using the following function:
int pthread_"utex_init(pthread_"utex_t *"utex,
const pthread_"utexattr_t *"attr
);
/he arguments of pthread_$utex_init are as follows:
$utex Input pointer to an instance of mute& lock o!'ect
$attr Input pointer to a structure that provides additional parameters for creating a custom
mute&. his argument is usually specified as ()** in most cases
5efore one can discard an initiali4ed instance of mutex loc$ ob&ect! the storage space allocated for the
internal attributes needs to be deallocated. /his is done using the following function:
int pthread_"utex_destro;(pthread_"utex_t *"utex);
/o loc$ a mutex! use the following function:
int pthread_"utex_loc:(pthread_"utex_t *"utex);
/o unloc$ a mutex! use the following function:
int pthread_"utex_unloc:(pthread_"utex_t *"utex);
or thread synchroni4ation using mutex! we will create an ob&ect wrapper that will abstract and hide
away the intricacies of mutex loc$ing mechanism and present a simple interface for the user. /his
abstraction will be encapsulated in the class named %oc& in all the following examples. /he following
is the definition of the %oc& class:
class 2oc: %
protected(
pthread_"utex_t "utex;

*revent cop;ing or assign"ent
2oc:(const 2oc:/ arg);
2oc:/ operator.(const 2oc:/ rhs);

pu<lic(
2oc:();
virtual =2oc:();
void loc:();
void unloc:();
,;
/he %oc& class is simple enough and does not need explanation.
/he following is a simple example that uses the abstract classes Thread and %oc&. /his example
demonstrates the use of mutex loc$ to guard access to a shared resource (class %hared) by two threads:
* -utex.cpp *
#include <iostrea">
#include <pthread.h>
using na"espace std;
----- #lass ( 6hread -----
class 6hread %
private(
pthread_t _id;

*revent cop;ing or assign"ent
6hread(const 6hread/ arg);
6hread/ operator.(const 6hread/ rhs);

protected(
<ool started;
void *arg;

static void *exec(void *thr);

pu<lic(
6hread();
virtual =6hread();
unsigned int tid() const;
void start(void *arg . 0122);
void join();
virtual void run() . +;
,;
6hread((6hread() ( started(false) %
cout << &6hread((6hread()& << endl;
,
6hread((=6hread() %
cout << &6hread((=6hread()& << endl;
,
unsigned int 6hread((tid() const %
return _id;
,

1ses default argu"ent( arg . 0122
void 6hread((start(void *arg) %
int ret;
if (3started) %
started . true;
this->arg . arg;

*
* 7ince pthread_create is a # li<rar; function, the >rd
* argu"ent is a glo<al function that $ill <e executed <;
* the thread. 'n #99, $e e"ulate the glo<al function using
* the static "e"<er function that is called exec. 6he ?
th
* argu"ent is the actual argu"ent passed to the function
* exec. !ere $e use this pointer, $hich is an instance of
* the 6hread class.
*
if ((ret . pthread_create(/_id, 0122, /6hread((exec, this)) 3.
+) %
cout << strerror(ret) << endl;
thro$ &4rror&;
,
,
,
void 6hread((join() %
@llo$ the thread to $ait for the ter"ination status
pthread_join(_id, 0122);
,
Aunction that is to <e executed <; the thread
void *6hread((exec(void *thr) %
reinterpret_cast<6hread *> (thr)->run();
,
----- #lass ( 2oc: -----
class 2oc: %
protected(
pthread_"utex_t "utex;

*revent cop;ing or assign"ent
2oc:(const 2oc:/ arg);
2oc:/ operator.(const 2oc:/ rhs);

pu<lic(
2oc:();
virtual =2oc:();
void loc:();
void unloc:();
,;
2oc:((2oc:() %
pthread_"utex_init(/"utex, 0122);
,
2oc:((=2oc:() %
pthread_"utex_destro;(/"utex);
,

void 2oc:((loc:() %
pthread_"utex_loc:(/"utex);
,
void 2oc:((unloc:() %
pthread_"utex_unloc:(/"utex);
,
----- #lass ( 7hared -----
class 7hared %
private(
int _cnt;

pu<lic(
7hared() ( _cnt(+) %,;
int get#nt();
void inc#nt();
,;
int 7hared((get#nt() %
return _cnt;
,
void 7hared((inc#nt() %
_cnt99;
,
----- #lass ( -;6hread -----
class -;6hread ( pu<lic 6hread %
private(
2oc: *_lc:;

pu<lic(
-;6hread() %
_lc: . ne$ 2oc:();
,

=-;6hread() %
delete _lc:;
,

*
* 6his "ethod $ill <e executed <; the 6hread((exec "ethod,
* $hich is executed in the thread of execution
*
void run() %
7hared *d . reinterpret_cast<7hared *> (arg);
for (;;) %
_lc:->loc:();
d->inc#nt();
cout << &6hread<& << tid() << &>( & << d->get#nt()
<< endl;
_lc:->unloc:();
sleep(5);
,
,
,;
----- -ain -----
int "ain() %
7hared data;
-;6hread thr5, thrC;
thr5.start(/data);
thrC.start(/data);
thr5.join();
thrC.join();
,
Condition Variables:
+ow we will explore condition variables. A condition variable is another synchroni4ation mechanism
where one or more threads will relinquish a loc$ and wait for an event of interest to occur. 1hen an
event of interest happens! another thread will notify the waiting threads of the occurrence of the
interested event. A #ondition variable is always used in con&unction with a mutex loc$.
"roducer.#onsumer problem is a classic example that use condition variables.
A condition variable ob&ect is encapsulated in a structure pthread_cond_t. "rior to use! an instance of
the condition variable needs to be initiali4ed! which allocates storage for the internal attributes on the
ob&ect. /his is done using the following function:
int pthread_cond_init(pthread_cond_t *cond,
const pthread_condattr_t *cattr
);
/he arguments of pthread_cond_init are as follows:
cond Input pointer to an instance of condition varia!le o!'ect
cattr Input pointer to a structure that provides additional parameters for creating a custom
condition varia!le. his argument is usually specified as ()** in most cases
5efore one can discard an initiali4ed instance of condition variable ob&ect! the storage space allocated
for the internal attributes needs to be deallocated. /his is done using the following function:
int pthread_cond_destro;(pthread_cond_t *cond);
/o wait on a condition or event! use the following function:
int pthread_cond_$ait(pthread_cond_t *cond,
pthread_"utex_t *"utex
);
+otice that the above function needs a mutex loc$ to be used in con&unction with the condition
variable. #all to this function causes the thread to release the mutex loc$ and bloc$ on the condition
until the condition or event occurs.
/o notify a condition or event! use the following function:
int pthread_cond_signal(pthread_cond_t *cond);
or using condition variables! we will create an ob&ect wrapper that will abstract and hide away the
intricacies of the mechanism and present a simple interface for the user. /his abstraction will be
encapsulated in the class named Condition in all the following example. /he following is the definition
of the Condition class:
class #ondition ( pu<lic 2oc: %
protected(
pthread_cond_t cond;

*revent cop;ing or assign"ent
#ondition(const #ondition/ arg);
#ondition/ operator.(const #ondition/ rhs);

pu<lic(
#ondition();
virtual =#ondition();
void $ait();
void notif;();
,;
A condition variable occurs in con&unction with a mutex loc$ and hence the Condition class extends the
%oc& class. /he Condition class is simple enough and does not need explanation.
/he following is a simple example that uses the abstract classes Thread! %oc&! and Condition. /his
example demonstrates the classic "roducer.#onsumer problem using three threads:
* #ondition *
#include <iostrea">
#include <list>
#include <stdli<.h>
#include <pthread.h>
using na"espace std;
----- #lass ( 6hread -----
class 6hread %
private(
pthread_t _id;

*revent cop;ing or assign"ent
6hread(const 6hread/ arg);
6hread/ operator.(const 6hread/ rhs);

protected(
<ool started;
void *arg;

static void *exec(void *thr);

pu<lic(
6hread();
virtual =6hread();
unsigned int tid() const;
void start(void *arg . 0122);
void join();
virtual void run() . +;
,;
6hread((6hread() ( started(false) %
cout << &6hread((6hread()& << endl;
,
6hread((=6hread() %
cout << &6hread((=6hread()& << endl;
,
unsigned int 6hread((tid() const %
return _id;
,
1ses default argu"ent( arg . 0122
void 6hread((start(void *arg) %
int ret;
if (3started) %
started . true;
this->arg . arg;

*
* 7ince pthread_create is a # li<rar; function, the >rd
* argu"ent is a glo<al function that $ill <e executed <;
* the thread. 'n #99, $e e"ulate the glo<al function using
* the static "e"<er function that is called exec. 6he ?
th
* argu"ent is the actual argu"ent passed to the function
* exec. !ere $e use this pointer, $hich is an instance of
* the 6hread class.
*
if ((ret . pthread_create(/_id, 0122, /6hread((exec, this)) 3.
+) %
cout << strerror(ret) << endl;
thro$ &4rror&;
,
,
,
void 6hread((join() %
@llo$ the thread to $ait for the ter"ination status
pthread_join(_id, 0122);
,
Aunction that is to <e executed <; the thread
void *6hread((exec(void *thr) %
reinterpret_cast<6hread *> (thr)->run();
,
----- #lass ( 2oc: -----
class 2oc: %
protected(
pthread_"utex_t "utex;

*revent cop;ing or assign"ent
2oc:(const 2oc:/ arg);
2oc:/ operator.(const 2oc:/ rhs);

pu<lic(
2oc:();
virtual =2oc:();
void loc:();
void unloc:();
,;
2oc:((2oc:() %
pthread_"utex_init(/"utex, 0122);
,
2oc:((=2oc:() %
pthread_"utex_destro;(/"utex);
,
void 2oc:((loc:() %
pthread_"utex_loc:(/"utex);
,
void 2oc:((unloc:() %
pthread_"utex_unloc:(/"utex);
,
----- #lass ( #ondition -----
class #ondition ( pu<lic 2oc: %
protected(
pthread_cond_t cond;

*revent cop;ing or assign"ent
#ondition(const #ondition/ arg);
#ondition/ operator.(const #ondition/ rhs);

pu<lic(
#ondition();
virtual =#ondition();
void $ait();
void notif;();
,;
#ondition((#ondition() %
pthread_cond_init(/cond, 0122);
,
#ondition((=#ondition() %
pthread_cond_destro;(/cond);
,
void #ondition(($ait() %
pthread_cond_$ait(/cond, /"utex);
,

void #ondition((notif;() %
pthread_cond_signal(/cond);
,
----- #lass ( Go< -----
@n interface that represents $or: to <e done
class Go< %
pu<lic(
virtual void $or:(const 6hread/ arg) . +;
,;
t;pedef Go< * Go<*tr;
----- #lass ( *rintGo< -----
@ concrete i"ple"entation of the Go< interface that represents
print $or: to <e done
class *rintGo< ( pu<lic Go< %
pu<lic(
void $or:(const 6hread/ arg);
,;
void *rintGo<(($or:(const 6hread/ arg) %
int t" . rand() H I;
cout << &*rintGo<(($or:(), 6hread . & << arg.tid() << &, )ait . & <<
t" << endl;
sleep(t");
,
----- #lass ( Go<Jueue -----
*
* @ Kueue data structure $hich represnts a collection of Go<s to <e handled
* <; consu"ers
*
class Go<Jueue %
private(
list<Go<*tr> _Kueue;
#ondition *_cnd;

pu<lic(
Go<Jueue();
virtual =Go<Jueue();
void enKueue(const 6hread/ arg, Go<*tr jp);
Go<*tr deKueue(const 6hread/ arg);
,;
Go<Jueue((Go<Jueue() %
_cnd . ne$ #ondition();
,
Go<Jueue((=Go<Jueue() %
delete _cnd;
,
void Go<Jueue((enKueue(const 6hread/ arg, Go<*tr jp) %
_cnd->loc:();
_Kueue.push_<ac:(jp);
cout << &6hread . & << arg.tid() << & added jo<& << endl;
_cnd->notif;();
_cnd->unloc:();
,
Go<*tr Go<Jueue((deKueue(const 6hread/ arg) %
Go<*tr jp . 0122;

_cnd->loc:();
$hile (3 jp) %
if (_Kueue.e"pt;()) %
cout << &6hread . & << arg.tid() << & to $ait& << endl;
_cnd->$ait();
,
jp . _Kueue.front();
if (jp) %
_Kueue.pop_front();
<rea:;
,
,
_cnd->unloc:();

return jp;
,
----- #lass ( *roducer6hread -----
'nstance of this 6hread produces Go<s
class *roducer6hread ( pu<lic 6hread %
pu<lic(
*
* 6his "ethod $ill <e executed <; the 6hread((exec "ethod,
* $hich is executed in the thread of execution
*
void run() %
Go<Jueue *K . reinterpret_cast<Go<Jueue *> (arg);

for (;;) %
K->enKueue(*this, ne$ *rintGo<());
sleep(rand() H I);
,
,
,;
----- #lass ( #onsu"er6hread -----
'nstance of this 6hread $or:s on Go<s
class #onsu"er6hread ( pu<lic 6hread %
pu<lic(
*
* 6his "ethod $ill <e executed <; the 6hread((exec "ethod,
* $hich is executed in the thread of execution
*
void run() %
Go<Jueue *K . reinterpret_cast<Go<Jueue *> (arg);

for (;;) %
Go<*tr jp . K->deKueue(*this);
jp->$or:(*this);
delete jp;
,
,
,;
----- -ain -----
int "ain() %
Go<Jueue jK;
*roducer6hread pth;
#onsu"er6hread cth5, cthC;
pth.start(/jK);
cth5.start(/jK);
cthC.start(/jK);
pth.join();
cth5.join();
cthC.join();
,
1ith this! we come to the end of this guide and hope it serves as a useful and practical reference.

Вам также может понравиться