Multithreading in C# - pitfalls, mistakes and solutions. Goyello Olivia Tech Talks 20014_08
Multithreading using C
description
Transcript of Multithreading using C
MultithreadingMultithreading using C
Version 0.1 ● 19 April 2011
© 2011 Faisal Khan - www.FaysalKhan.org
licensed under the terms of the GNU Free Documentation License Version 1.3 or later
Multithreading using C
Multithreading
VERSION: 0.1 REVISION DATE: 19 April 2011
www.FaysalKhan.Org
Table Of Contents1. What is a Thread?................................................................................................................1
2. The Program......................................................................................................................... 2
3. Why is Synchronization needed?.........................................................................................5
4. Problems.............................................................................................................................. 6
5. What is a Mutex?.................................................................................................................. 7
6. The Program......................................................................................................................... 8
7. What are Condition Variables?...........................................................................................11
8. The Program.......................................................................................................................12
9. Exercises............................................................................................................................ 16
Related Books........................................................................................................................ 25
Multithreading 0.1 i
www.FaysalKhan.Org
1. What is a Thread?State of the art operating systems can run several processes in parallel. Besides this is
a great thing, there are some disadvantages: slow context switch, overhead in
communication between the processes. To take care of those problems, there are
Threads.
Threads run within the context of a process and have the following characteristics:
1. They run concurrently.
2. They have no separate address space. They all use the context of the
surrounding process.
3. Thread switches are very fast because the CPU don't have to switch the
entire context of the process.
In fact, there is a disadvantage resulting from the points above: using processes the
operating system takes care of the address spaces, different from threads. Using
threads means that the programmer has to take care of starting, stopping even the
access to shared memory space. This makes a multithreaded program hard to debug,
but: it is fun!
There are two types of threads:
1. Usermode threads: run bound to one CPU
2. Kernelmode threads: run distributed over all CPUs in the system, if you have
a SMP system.
Let's have a look at a very first and simple program.
Multithreading 0.2 1
www.FaysalKhan.Org
2. The ProgramFirst I'd like to have a look at a very simple program. It's so obvious what it does, I
don't think it needs any explanation.
0 #include <stdio.h>
1
2 void * work(void * ptr)
3 {
4 int i;
5 for (i = 0; i < 10; i++)
6 {
7 printf("%d", (int)ptr);
8 usleep(1000);
9 }
10 return 0;
11 }
12
13 int main(int argc, char ** argv)
14 {
15 work((void *)0);
16 work((void *)1);
17 return 0;
18 }
The program start, it calls the function work twice in series and terminates. The
function work prints the specified number 10 times and waits for 1 second after every
time. Just plain and simple.
Since this is a tutorial about multithreading, let's change the program in order to let
the function workd run at the same time instead of in series. To reach this goal we
have to do a few things (changes are marked bold):
0 #include <stdio.h>
1 #include <pthread.h>
2
3 void * work(void * ptr)
4 {
5 int i;
6 for (i = 0; i < 10; i++)
Multithreading 0.2 2
www.FaysalKhan.Org
7 {
8 printf("%d", (int)ptr);
9 usleep(1000);
10 }
11 pthread_exit(0);
12 }
13
14 int main(int argc, char ** argv)
15 {
16 pthread_t t0, t1;
17
18 pthread_create(&t0, 0, work, (void *)0);
19 pthread_create(&t1, 0, work, (void *)1);
20
21 pthread_join(t0, 0);
22 pthread_join(t1, 0);
23 return 0;
24 }
2.1 Walk through
The first thing we've changed is to include another header on line 1. We need this
header in order to get able to use the pthreadlibrary.
1 #include <pthread.h>
The function work has only one change: the exit of the function (line 11). This is
needed to terminate the thread properly.
11 pthread_exit(0);
Most of the changes appear in the function main. To start the threads we have to
define an ID for each:
16 pthread_t t0, t1;
The second step is to create the threads. Please note: if you create a thread, it will run
immediately!
18 pthread_create(&t0, 0, work, (void *)0);
19 pthread_create(&t1, 0, work, (void *)1);
Have a look at the man pages for an explenation of all parameters. The two most
important are the first (the ID) and the third (the function to be called).
As mentioned in the introduction What is s Thread?, the programmer (as we are) has
to take care of the entire handling, including the proper termination. So we have two
choices:
Multithreading 0.2 3
www.FaysalKhan.Org
1. wait for the created threads to terminate
2. detach the threads
We choose no. 1, for now. Ok, let's wait for the threads:
21 pthread_join(t0, 0);
22 pthread_join(t1, 0);
There we are! Our first multithreaded program. Frankly, it does nothing outstanding,
but it works and gives you a good access to the world of multithreading.
As mentioned before, I assume you to know how to compile and run C programs. So no
explanations here, sorry.
Multithreading 0.2 4
www.FaysalKhan.Org
3. Why is Synchronization needed?Whenever two or more active parts have to share resources, sooner or later appear
conflicts about who uses when a shared resource. Synchronization of those active
parts makes it possible to control the access to the resources and lets run the
application stable and reliable.
Stability and reliability are very important attributes when it comes to let several
therads running concurrently. Assuming the mutual exclusion is guaranteed, there are
two problems we have to take care of:
1. Deadlock
2. indefinite Suspension
Read below to learn more about those problems.
Multithreading 0.2 5
www.FaysalKhan.Org
4. ProblemsAs mentioned above there are several problems the programmer has to take care of.
This section covers those problems and how they can occur.
4.1 Deadlock
What is a deadlock? A deadlock is the situation in which all threads are blocked and
waiting for a resource. The picture below shows how this situation can happen:
Figure 1: an example of how a deadlock can happen.
The thread X holds the resource A, the thread Y holds the resource B. Now the
thread X wants to take the resource B and get blocked, because thread Y is already
using it. How comes, thread Y likes to get the resource A as well and get blocked too,
because thread X already holds it. This is a deadlock situation, because neither
thread X nor thread Y is able to continue its work.
It is necessary to avoid this situation at any cost. To gain performance at the cost of
the risk of a deadlock is a bad choice.
One possibility to avoid deadlock is to synchronize the threads and let them fetch the
resources using timeouts instead of blocking. This means that the thread which likes to
get the resource will fail after an amount of time. This means of course a good
exception/error handling.
4.2 Indefinite Suspension
Indefinite suspension can occur if one thread is of lower priority than another and the
scheduler run always the thread with the highest priority (not all schedulers do so). If
the high priority thread has a big workload, the lower priority thread does not get CPU
time any more, for an indefinite amount of time.
This is mostly an issue of design. A good design would be, that your program does run
(logically) even if you don't consider the priorities. They should only be for making
adjustments in response times (in theory).
Multithreading 0.2 6
www.FaysalKhan.Org
5. What is a Mutex?A the name says: mutual exclusion is a mechanism to capsule a part of the program,
so called critical sections. It makes sure, that only one thread can be inside of a critical
section. Of course if there are several critical section, each guarded by a independent
mutex, it is possible to have a thread X in the section A and another thread Y in the
section B.
If a thread is within the critical section guarded by a mutex, it is called "the thread is
holding the mutex".
The consequence of that mechanism is, that the programmer should try to keep the
cirital sections as short and simple as possible. Not to respect this rule means a
possible (probably) loss of (a lot of) performance.
Mainly there are three operations on a mutex, which are used for daily business:
• lock: locks the mutex
• unlock: unlocks the mutex
• trylock: tries to lock the mutex, non-blocking function. If the mutex is already
locked, the function will return immediatly providing an error code. In the other
case, the mutex is locked and the thread may proceed.
The critical section usually looks like this:
1. lock the mutex
2. do the critical stuff
3. unlock the mutex
In most cases it is very useful to encapsulate the critical section within a separate
function. This enhances readability and maintainability.
Multithreading 0.2 7
www.FaysalKhan.Org
6. The ProgramEnough for the theory, let's have a look at an example.
We start with the program we made in the last tutorial (thread1.c):
0 #include <stdio.h>
1 #include <pthread.h>
2
3 void * work(void * ptr)
4 {
5 int i;
6 for (i = 0; i < 10; i++)
7 {
8 printf("%d", (int)ptr);
9 usleep(1000);
10 }
11 pthread_exit(0);
12 }
13
14 int main(int argc, char ** argv)
15 {
16 pthread_t t0, t1;
17
18 pthread_create(&t0, 0, work, (void *)0);
19 pthread_create(&t1, 0, work, (void *)1);
20
21 pthread_join(t0, 0);
22 pthread_join(t1, 0);
23 return 0;
24 }
In the function work there is a printf statement which prints out the specified number
for the thread. Now we like to write more than one character. but since the
function printf is not thread safe, we have to take care of problem by ourselfes.
To reach our goal we're using a mutex (all changes to the original program are marked
bold):
0 #include <stdio.h>
1 #include <pthread.h>
2
Multithreading 0.2 8
www.FaysalKhan.Org
3 pthread_mutex_t mtx;
4
5 void print(int thread, int i)
6 {
7 pthread_mutex_lock(&mtx);
8 printf("thread %d: %d\n", thread, i);
9 pthread_mutex_unlock(&mtx);
10 }
11
12 void * work(void * ptr)
13 {
14 int i;
15 for (i = 0; i < 10; i++)
16 {
17 print((int)ptr, i);
18 usleep(1000);
19 }
20 pthread_exit(0);
21 }
22
23 int main(int argc, char ** argv)
24 {
25 pthread_t t0, t1;
26
27 pthread_mutex_init(&mtx, 0);
28 pthread_create(&t0, 0, work, (void *)0);
29 pthread_create(&t1, 0, work, (void *)1);
30
31 pthread_join(t0, 0);
32 pthread_join(t1, 0);
33
34 pthread_mutex_destroy(&mtx);
35 return 0;
36 }
6.1 Walkthrough
First, we have to define a mutex variable for further use.
3 pthread_mutex_t mtx;
Multithreading 0.2 9
www.FaysalKhan.Org
Since we like to encapsulate the critical section within a separate function to increase
readability, let's do so and we have to protect the critical section with locking and
unlocking the mutex:
5 void print(int thread, int i)
6 {
7 pthread_mutex_lock(&mtx);
8 printf("thread %d: %d\n", thread, i);
9 pthread_mutex_unlock(&mtx);
10 }
The thread function a small change to according to the separated critical section:
17 print((int)ptr, i);
All there is left to do is within the function main to take care of the initialisation
27 pthread_mutex_init(&mtx, 0);
and the destruction of the mutex variable:
34 pthread_mutex_destroy(&mtx);
Multithreading 0.2 10
www.FaysalKhan.Org
7. What are Condition Variables?Condition variables are a very elegant way to do some synchronization. A thread can
wait for a condition becoming true to continue and a thread can also make a condition
come true. Since a thread is an active part, it can wait for or make a condition which
are others waiting for.
Let's have a look at an example (see figure 1 below). Thread X creates an item and
stores it. Thread Y is waiting for the item to process it. Now let's say: "thread X is
making a condition for thread Y to get to work."
Figure 1: making and waiting for a condition.
We will have a look at an implementation of this example below.
Multithreading 0.2 11
www.FaysalKhan.Org
8. The ProgramThis is the entire source code of the example. For a walk through, please read below.
The following program is a very simple producer-consumer demo. It shows two
threads, one that produces some numbers, the second that consumes them. The demo
is simple because we only use one place as buffer between those threads. So every
time the producer has an item, it has to wait for the consumer to take it. To tell the
consumer (sending a signal), that an item is ready we use the condition variable.
As soon as the ten items are produced and consumed, the threads will stop and the
demo will end.
0 #include <stdio.h>
1 #include <pthread.h>
2
3 pthread_mutex_t mtx;
4 pthread_cond_t cond;
5
6 int how_many = 10;
7 int pool = 0;
8
9 void * producer(void * ptr)
10 {
11 while (how_many > 0)
12 {
13 pthread_mutex_lock(&mtx);
14 printf("producer: %d\n", how_many);
15 pool = how_many;
16 how_many--;
17 pthread_mutex_unlock(&mtx);
18 pthread_cond_signal(&cond);
19 }
20 pthread_exit(0);
21 }
22
23 void * consumer(void * ptr)
24 {
25 while (how_many > 0)
26 {
27 pthread_mutex_lock(&mtx);
Multithreading 0.2 12
www.FaysalKhan.Org
28 pthread_cond_wait(&cond, &mtx);
29 printf("consumer: %d\n", pool);
30 pool = 0;
31 pthread_mutex_unlock(&mtx);
32 }
33 pthread_exit(0);
34 }
35
36 int main(int argc, char ** argv)
37 {
38 pthread_t prod, cons;
39 pthread_mutex_init(&mtx, 0);
40 pthread_cond_init(&cond, 0);
41 pthread_create(&cons, 0, consumer, 0);
42 pthread_create(&prod, 0, producer, 0);
43 pthread_join(prod, 0);
44 pthread_join(cons, 0);
45 pthread_cond_destroy(&cond);
46 pthread_mutex_destroy(&mtx);
47 return 0;
48 }
8.1 Walkthrough
The first part is plain simple header inclusion.
0 #include <stdio.h>
1 #include <pthread.h>
Next we have to define some global variables. Global variables are not my favorite,
but they will do for this example program. My usually advise: Do not use global
data! Ok, the data we need is a mutex and a condition variable.
3 pthread_mutex_t mtx;
4 pthread_cond_t cond;
We also need some variables which take data for the simple producer-consumer demo.
6 int how_many = 10;
7 int pool = 0;
Next we'll have a look at the consumer:
9 void * producer(void * ptr)
10 {
11 while (how_many > 0)
12 {
Multithreading 0.2 13
www.FaysalKhan.Org
13 pthread_mutex_lock(&mtx);
14 printf("producer: %d\n", how_many);
15 pool = how_many;
16 how_many--;
17 pthread_mutex_unlock(&mtx);
Nothing special so far. The thread locks the mutex to enter the critical section, prints
out which item has been produced, stores it in the pool and decreases the total
number of item to produce in the future. The last step is to unlock the mutex; to exit
the critical section.
The new thing we're looking at in this tutorial is the condition variable and their
handling. So far we didn't really used the condition variable at all, but now we have to
send a signal that the pool is full:
18 pthread_cond_signal(&cond);
What now follows is the usual thread termination statement.
19 }
20 pthread_exit(0);
21 }
Let's have a look at the consumer.
23 void * consumer(void * ptr)
24 {
25 while (how_many > 0)
26 {
27 pthread_mutex_lock(&mtx);
So far, there's nothing special, locking the mutex to enter the critical section is
standard procedure. Now appears the question, what if there aren't any produced
items yet? Here comes the answer: the condition variable that we are waiting for.
28 pthread_cond_wait(&cond, &mtx);
This statement will unlock the mutex until the condition gets true, say a signal has
been sent. Then the mutex gets locked again (using pthread_mutex_lock, so no
timeout), and the thread continues into the critical section.
29 printf("consumer: %d\n", pool);
30 pool = 0;
31 pthread_mutex_unlock(&mtx);
32 }
33 pthread_exit(0);
34 }
The rest of the function is quite normal: printing the data from the pool, resetting the
pool and unlocking the mutex, exiting the critical section.
Multithreading 0.2 14
www.FaysalKhan.Org
Last but not least, standard procedure to terminate the thread.
The function main is quite normal too. The only difference are the initialisation and the
destruction of the condition variable.
Please note: the consumer thread is started first, so it can start to consume as soon as
there are items ready. This is a measure of quality, not to run the producer first.
36 int main(int argc, char ** argv)
37 {
38 pthread_t prod, cons;
39 pthread_mutex_init(&mtx, 0);
40 pthread_cond_init(&cond, 0);
41 pthread_create(&cons, 0, consumer, 0);
42 pthread_create(&prod, 0, producer, 0);
43 pthread_join(prod, 0);
44 pthread_join(cons, 0);
45 pthread_cond_destroy(&cond);
46 pthread_mutex_destroy(&mtx);
47 return 0;
48 }
Multithreading 0.2 15
www.FaysalKhan.Org
9. ExercisesMultithreading is fun, isn't it? Go ahead try to implement a producer-consumer system:
a. using a circular buffer (queue) with more than one space
b. using more than one producer
c. using more than one consumer
d. using a queue larger than 1, using multiple producers and consumers
9.1 Solutions
The solution provided here is not the only one possible implementations. The are many
solutions. This ones should give you a hint or if you like to compare your solution with
this ones.
a.
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#define POOL_SIZE 3
typedef struct
{
int data[POOL_SIZE];
int start;
int end;
int count;
} pool_t;
int num_of_items = 10;
pthread_mutex_t mtx;
pthread_cond_t cond_nonempty;
pthread_cond_t cond_nonfull;
pool_t pool;
void init_pool(pool_t * pool)
{
pool->start = 0;
pool->end = -1;
pool->count = 0;
Multithreading 0.2 16
www.FaysalKhan.Org
}
void push(pool_t * pool, int data)
{
pthread_mutex_lock(&mtx);
while (pool->count >= POOL_SIZE)
{
pthread_cond_wait(&cond_nonfull, &mtx);
}
pool->end = (pool->end + 1) % POOL_SIZE;
pool->data[pool->end] = data;
pool->count++;
pthread_mutex_unlock(&mtx);
}
int pop(pool_t * pool)
{
int data = 0;
pthread_mutex_lock(&mtx);
while (pool->count <= 0)
{
pthread_cond_wait(&cond_nonempty, &mtx);
}
data = pool->data[pool->start];
pool->start = (pool->start + 1) % POOL_SIZE;
pool->count--;
pthread_mutex_unlock(&mtx);
return data;
}
void * producer(void * ptr)
{
while (num_of_items > 0)
{
push(&pool, num_of_items);
printf("producer: %d\n", num_of_items);
num_of_items--;
pthread_cond_broadcast(&cond_nonempty);
}
pthread_exit(0);
}
Multithreading 0.2 17
www.FaysalKhan.Org
void * consumer(void * ptr)
{
while (num_of_items > 0 || pool.count > 0)
{
printf("consumer: %d\n", pop(&pool));
pthread_cond_broadcast(&cond_nonfull);
usleep(500000);
}
pthread_exit(0);
}
int main(int argc, char ** argv)
{
pthread_t cons, prod;
init_pool(&pool);
pthread_mutex_init(&mtx, 0);
pthread_cond_init(&cond_nonempty, 0);
pthread_cond_init(&cond_nonfull, 0);
pthread_create(&cons, 0, consumer, 0);
pthread_create(&prod, 0, producer, 0);
pthread_join(prod, 0);
pthread_join(cons, 0);
pthread_cond_destroy(&cond_nonempty);
pthread_cond_destroy(&cond_nonfull);
pthread_mutex_destroy(&mtx);
return 0;
}
b.
#include <stdio.h>
#include <pthread.h>
#define FULL 1
#define EMPTY 0
pthread_mutex_t mtx_item;
pthread_mutex_t mtx;
pthread_cond_t cond_nonempty;
pthread_cond_t cond_nonfull;
Multithreading 0.2 18
www.FaysalKhan.Org
int how_many = 10;
int pool = 0;
int pool_state = EMPTY;
int produce_one(void)
{
int result = -1;
pthread_mutex_lock(&mtx_item);
result = how_many--;
pthread_mutex_unlock(&mtx_item);
return result;
}
void push(int data)
{
pthread_mutex_lock(&mtx);
while (pool_state == FULL) pthread_cond_wait(&cond_nonfull, &mtx);
pool = data;
pool_state = FULL;
pthread_mutex_unlock(&mtx);
}
int pop(void)
{
int data;
pthread_mutex_lock(&mtx);
while (pool_state == EMPTY)
{
if (how_many <= 0)
{
pthread_mutex_unlock(&mtx);
return -1;
}
pthread_cond_wait(&cond_nonempty, &mtx);
}
data = pool;
pool_state = EMPTY;
pthread_mutex_unlock(&mtx);
return data;
}
Multithreading 0.2 19
www.FaysalKhan.Org
void * producer(void * ptr)
{
int data;
while (how_many > 0)
{
data = produce_one();
printf("producer<%d>: %d\n", (int)ptr, data);
push(data);
pthread_cond_broadcast(&cond_nonempty);
}
pthread_exit(0);
}
void * consumer(void * ptr)
{
while (how_many > 0 || pool_state != EMPTY)
{
printf("consumer<%d>: %d\n", (int)ptr, pop());
pthread_cond_broadcast(&cond_nonfull);
}
pthread_exit(0);
}
int main(int argc, char ** argv)
{
pthread_t prod, cons0, cons1;
pthread_mutex_init(&mtx_item, 0);
pthread_mutex_init(&mtx, 0);
pthread_cond_init(&cond_nonempty, 0);
pthread_cond_init(&cond_nonfull, 0);
pthread_create(&cons0, 0, consumer, (void *)0);
pthread_create(&cons1, 0, consumer, (void *)1);
pthread_create(&prod, 0, producer, (void *)0);
pthread_join(prod, 0);
pthread_join(cons0, 0);
pthread_join(cons1, 0);
pthread_cond_destroy(&cond_nonempty);
pthread_cond_destroy(&cond_nonfull);
pthread_mutex_destroy(&mtx);
Multithreading 0.2 20
www.FaysalKhan.Org
pthread_mutex_destroy(&mtx_item);
return 0;
}
c.
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <time.h>
#define POOL_SIZE (3)
typedef struct
{
int data[POOL_SIZE];
int start;
int end;
int count;
} pool_t;
int items_to_produce = 20;
pthread_mutex_t mtx;
pthread_mutex_t mtx_item_prod;
pthread_cond_t cond_nonempty;
pthread_cond_t cond_nonfull;
pool_t pool;
void init_pool(pool_t * pool)
{
pool->start = 0;
pool->end = -1;
pool->count = 0;
}
void push(pool_t * pool, int data)
{
pthread_mutex_lock(&mtx);
while (pool->count >= POOL_SIZE) {
pthread_cond_wait(&cond_nonfull, &mtx);
}
Multithreading 0.2 21
www.FaysalKhan.Org
pool->end = (pool->end + 1) % POOL_SIZE;
pool->data[pool->end] = data;
pool->count++;
pthread_mutex_unlock(&mtx);
}
int pop(pool_t * pool)
{
int data = 0;
pthread_mutex_lock(&mtx);
while (pool->count <= 0) {
pthread_cond_wait(&cond_nonempty, &mtx);
}
data = pool->data[pool->start];
pool->start = (pool->start + 1) % POOL_SIZE;
pool->count--;
pthread_mutex_unlock(&mtx);
return data;
}
int size(pool_t * pool)
{
int result = 0;
pthread_mutex_lock(&mtx);
result = pool->count;
pthread_mutex_unlock(&mtx);
return result;
}
int produce_one(void)
{
int result = -1;
pthread_mutex_lock(&mtx_item_prod);
result = items_to_produce--;
pthread_mutex_unlock(&mtx_item_prod);
return result;
}
int has_more_to_produce(void)
{
int result = 0;
Multithreading 0.2 22
www.FaysalKhan.Org
pthread_mutex_lock(&mtx_item_prod);
result = (items_to_produce > 0);
pthread_mutex_unlock(&mtx_item_prod);
return result;
}
void * producer(void * ptr)
{
int data;
while (has_more_to_produce()) {
data = produce_one();
printf("producer %d : %d\n", (int)ptr, data);
push(&pool, data);
pthread_cond_broadcast(&cond_nonempty);
usleep(500 + (rand() % 500)); // slow down for demonstration
}
pthread_exit(0);
}
void * consumer(void * ptr)
{
while (items_to_produce > 0 || size(&pool) > 0) {
printf("consumer %d : %d\n", (int)ptr, pop(&pool));
pthread_cond_broadcast(&cond_nonfull);
usleep(1000 + (rand() % 2000)); // slow down for demonstration
}
pthread_exit(0);
}
int main(int argc, char ** argv)
{
pthread_t cons0, cons1, cons2; // three consumers
pthread_t prod0, prod1; // two producers
// initialize pseudo random generator
srand((unsigned int)time(0));
// initialize buffer related stuff
init_pool(&pool);
pthread_mutex_init(&mtx, 0);
pthread_cond_init(&cond_nonempty, 0);
Multithreading 0.2 23
www.FaysalKhan.Org
pthread_cond_init(&cond_nonfull, 0);
// create multiple consumers, waiting for data
pthread_create(&cons0, 0, consumer, (void *)0);
pthread_create(&cons1, 0, consumer, (void *)1);
pthread_create(&cons2, 0, consumer, (void *)2);
// create multiple producers
pthread_mutex_init(&mtx_item_prod, 0);
pthread_create(&prod0, 0, producer, (void *)0);
pthread_create(&prod1, 0, producer, (void *)1);
// wait for all threads to terminate
pthread_join(prod0, 0);
pthread_join(prod1, 0);
// clean up buffer related stuff
pthread_cond_destroy(&cond_nonempty);
pthread_cond_destroy(&cond_nonfull);
pthread_mutex_destroy(&mtx);
// consumers get automatically terminated in this example
return 0;
}
Multithreading 0.2 24
www.FaysalKhan.Org
Related Books• Windows via C/C++ by Jeffrey M. Richter and Christopher Nasarre. (The book is not
only about multithreading but has got a good section devoted to it.)
• Effective Concurrency by Herb Sutter.
• C++ Concurrency in Action by Anthony Williams.
• Programming with POSIX Threads by David R. Butenhof.
• Multithreaded Programming in C++ by Mark Walmsley.
• Concurrent Programming on Windows by Joe Duffy.
Multithreading 0.2 25