1. Goals for the next two weeks:
-
Write multi-threaded programs with the
pthreadlibrary -
Learn some Unix commands for finding out information about CPUs and threads running in the system
-
Reminder about ParaVis library and a pthread ParaVis example
-
Practice with some debugging techniques for
pthreadprograms
2. Starting Point Code
Start by creating a week12 directory in your WeeklyLabs subdirectory
and copying over some files:
$ cd ~/cs31/WeeklyLabs
$ mkdir week12
$ cd week12
$ pwd
/home/you/cs31/WeeklyLabs/week12
$ cp ~newhall/public/cs31/week12/* ./
$ ls
2.1. Small pthread programs
The files first.c, second.c, and third.c provide examples of small
pthread programs. Note that we also have a video walk through of
these three example pthread programs.
2.1.1. first.c
In first.c, the program creates two threads using pthread_create, then
waits for each to finish using pthread_join. The main function hello for
each thread prints out a message then returns.
Note the prototype for the thread main function. You must use the void *
return type and void *arg as the parameter list, even if your thread main
function does not need input or return a value.
2.1.2. second.c
In second.c, we show how to pass a thread ID generated by the program to each
thread. This program shows that the pthread library generates a
pthread_self() ID for each thread. This ID is used for pthread_join, but
the other local thread ID (0/1) is often useful for specifying which threads
execute which path through the code.
2.1.3. third.c
In third.c, we expand the input argument to a struct, and show how to
dynamically create array of structs. This program determines the number of
threads as a command line argument and divides "work" across all the threads.
In this small example, the work is specified by a total amount of time on the
command line, which is partitioned evenly across threads. If you run:
$ time ./third 5 1
$ time ./third 5 5
You should see that total time is faster with five threads, as they are able to run concurrently. Keep in mind that the threads don’t really need to use CPU resources while sleeping, so the performance gains are a quite exaggerated. You probably wouldn’t expect a program to run 100 times faster with 100 threads if you ran it on a machine with only 8 cores:
$ time ./third 30 1 # takes ~30 seconds, so run at your own discretion...
$ time ./third 30 100
3. After Checkpoint
Once you get past the checkpoint, return to the steps below.
4. Race conditions
We looked at the hello.c program together in class. It shows an
example of a simple pthread program that:
-
spawns some worker threads by calling
pthread_create -
passes parameter values (thread logical identifier values) to each thread’s main function via its
void *argsparameter. -
calls to
pthread_jointo wait for all spawned threads to exit.
Let’s try running it a few times with different numbers of threads.
This program is an example of a multi-threaded program with a race condition. We will try to fix it using some pthread synchronization primitives. But first, let’s look at the following program that has some examples of using pthread synchronization primitives.
5. Synchronization primitives
The synch.c program contains some examples using pthreads
mutex and barrier synchronization. It includes another example of defining
a struct type that can be used to pass several values to a thread’s main
function via its single void *args parameter. Try running this to
see understand what it is doing. Then go back and fix the hello.c
program by adding in mutex lock/unlock to remove the race condition.
6. top, htop, and threads
top and htop are Unix utilities that list information about processes
and threads and how they are using resources like memory and CPU.
If you run top with no command line options, then it displays
per-process statistics. If you run top with -H, top will display
statistics for individual threads (if you run the synch program
for a large number of threads, you can see them show up in top):
$ top -H
Let’s try out the example of configuring top from this page:
using top and
htop to change what top displays. Then we can try running a
multi-threaded process and see what top shows us.
Let’s run the synch program with a bunch of threads, and then top -H in
another window to see what we can see.
We can also try running htop.
We can also see the number of CPU cores on a particular machine, and a lot of
information about each one, by looking at a file in the /proc file system:
$ less /proc/cpuinfo
The less program allows you to scroll through the /proc/cpuinfo file one
page at a time using the space bar or use the up/down arrows to navigate. Type
q to quit.
7. ParaVis pthread example
We are not going to talk about this in lab, but with the in-lab code is an example visualization that uses the ParaVis animation library. This is very similar to the one we looked at in Weekly Lab 8, but it is an example of a parallel pthread animation. The comment at the top describes the sequence of ParaVis library calls that an application needs to make to visualize their computation.
8. debugging pthread programs
Debugging threaded programs can be tricky because there are multiple streams
of execution. In general, try to debug with as few threads as possible. When
you use printf, print out a thread id and call fflush after each printf.
You can also put printf in conditional statements to only have one of the
threads print out information (or only some of the threads, or only some of
the information, …). For example, if each thread is passed a logical thread
id value on start-up, and stores its value in a local variable named my_tid
then you could have logical thread 1 be the debug output printing thread to do
something like:
if (my_tid == 1) {
printf("tid:%d: value of count is now %d and i is %d\n",
my_tid, count, i);
fflush(stdout);
}
8.1. gdb and pthreads
gdb has support for debugging multi-threaded processes. If you want to try using gdb to debug your pthread code, here is some general information about it and an example you can try out:
Debugging
pthreads programs with gdb. It contains an example run of debugging the
racecond.c program you copied over with this week’s in-class code.
More detailed information about gdb and pthreads can be found: in Textbook Chapter 3.6
9. Handy References
-
C and Pthreads refs in the Textbook:
-
C strings and the string library (Chapt. 2.6)
-
C dynamically allocated 2D-arrays (Chapt. 2.5.2 Method 1)
-
C
void *type (Chapt. 2.9.3) -
Command line arguments (Chapt. 2.9.2)
-
C debugging tools: valgrind, gdb, gdb and pthreads (Chapt. 3.1-3.3, 3.6)
-
C
pthreadprogramming (Chapt. 14.2)
-
-
Unix
-
Appendix 2: Using Unix, in particular section 17.6 on htop and top
-