header("HTTP/1.1 301 Moved Permanently"); header("Location: http://softpixel.com/~cwright/programming/threads/threads.c.php"); exit(); include 'util.inc'; //Description("Multithreading in C/C++ using pthreads"); //Keywords("C, C++, Multithreading, posix, pthreads, thread, mutex, semaphore, examples"); headValid("HTML4.01S","Multithreading in C"); div("menu"); newMenu("Home",".."); newMenu("Programming Tutorials","index.php"); //newMenu("dabc","efg4/"); //newMenu(@readfile("don.inc"),""); ndiv(); div("content"); //div("title"); echo "
mutex
. A
mutex
is like a lock. A thread can lock it, and then any
subsequent attempt to lock it, by the same thread or any other, will cause
the attempting thread to block until the mutex
is unlocked.
These are very handy for keeping data structures correct from all
the threads' points of view. For example, imagine a very large linked
list. If one thread deletes a node at the same time that another thread
is trying to walk the list, it is possible for the walking thread to fall
off the list, so to speak, if the node is deleted or changed. Using a
mutex
to \"lock\" the list keeps this from happening.Mutex
stands for
Mutual Exclusion. synchronized
keyword.mutex
can unlock it, but sometimes
operating systems will allow any thread to unlock it. Doing this is, of course, a Bad Idea. If
you need this kind of functionality, read on about the semaphore
in the next
paragraph.mutex
is the semaphore
. A
semaphore
is like a mutex
that counts instead of
locks. If it reaches zero, the next attempt to access the semaphore will
block until someone else increases it. This is useful for resource
management when there is more than one resource, or if two separate
threads are using the same resource in coordination. Common
terminology for using semaphores is \"uping\" and \"downing\", where
upping increases the count and downing decreases and blocks on
zero. Semaphore
which does the
same thing, but uses acquire()
and release()
methods instead of uping and downing.semaphore
, even
Computer Scientists couldn't think up what this is short for.mutexes
, semaphores
are designed to allow multiple threads to up and down
them all at once. If you create a semaphore
with a count of 1, it will act just like
a mutex
, with the ability to allow other threads to unlock it.mutexes
and semaphores
as
Critical Sections. In general, it's a good idea to keep Critical Sections as short as
possible to allow the application to be as paralle as possible.pthread_t
for
thread identifiers, pthread_mutex_t
for mutexes, and
sem_t
for semaphores. We use the word \"pthread\" a lot
because it stands for POSIX Threads.");
newEntry("Compiling Multithreaded Programs",
"Compiling multithreaded applications will require a few minor tweaks to our build setup. First,
we'll need to include the appropriate header file. For POSIX systems, this header is called
pthread.h
. This header defines all the functions we'll be using to make threads. If
we're using semaphores
we'll also need to include semaphore.h
.#include <pthread.h>
#include <semaphore.h>
gcc
we simply use the -l
option, like this:gcc myProgram.o -o myProgram -lpthread
pthread_create
is used, and it takes 4 arguments.int pthread_create(pthread_t * pth, pthread_attr_t *att, void *
(*function), void * arg);
pthread_t
, where the
function stores the identifier of the newly-created thread. The next
argument is the attribute argument. This is typically
NULL
, but can also point to a structure that changes the
thread's attributes. the third argument is the function the new thread
will start at. If the thread returns from the function, the thread is
terminated as well. You can think of the function as
main
, since it behaves similarly. The final argument is
passed to the function when the thread is started. this is similar to
the argc/argv command line arguments to main
, but it can
be any data type. Zero is returned on success, otherwise a
failure of some variety happened.pthread_exit
. They behave
identically.pthread_detach
. A detached thread can't
be waited on.pthread_cancel
can help us accomplish this.int pthread_cancel(pthread_t thread);
pthread_cancel
is the thread identifier for the thread to be
cancelled. It returns zero if successful, or an error code otherwise.int pthread_setcancelstate
.");
newEntry("Mutexes and Semaphores",
"Mutexes are fairly easy to create. The function we use is
pthread_mutex_init
, which takes 2 parameters. The first
is a pointer to a mutex_t
that we're creating. The second
parameter is usually NULL
, but can also be a
pthread_mutexattr_t
structure that specifies different
attributes for it.pthread_mutex_lock
and
pthread_mutex_unlock
. These both take 1 parameter: a
pointer to the mutex being operated on.
pthread_mutex_trylock
is similar to
pthread_mutex_lock
, except that if it can't lock the
mutex, it returns a error instead of blocking.pthread_mutex_destroy
.sem_init
, which takes 3 parameters. The first is a pointer
to the semaphore being initialized. The second is always zero. This
argument is used to denote semaphores shared between processes, but it
isn't always supported. The third argument specifies the initial value of
the newly created semaphore.sem_post
. To \"Down\" a
semaphore, use sem_wait
. These kind of parallel
pthread_mutex_lock
and
pthread_mutex_unlock
.sem_destroy
is used to destroy a semaphore once it is no
longer needed.");
newEntry("Multithreading — Waiting for other threads",
"It is also possible to make one thread stop and wait for another thread
to finish. This is accomplished with pthread_join
. This
function takes a pthread_t
identifier to pick which thread to
wait for, and takes a void **
parameter to capture the return
value. Joining a thread that has already exited is possible, and
performing this will free any resources the thread had not already
deallocated. In GNU/Linux,
as well as other UNIX-like
operating systems, these unjoined threads are called zombies
.pthread_detach
) can't be waited on either.pthread_join
:
#include <stdio.h>
#include <pthread.h>
/* This is our thread function. It is like main(), but for a thread */
void *threadFunc(void *arg)
{
char *str;
int i = 0;
str=(char*)arg;
while(i < 10 )
{
usleep(1);
printf(\"threadFunc says: %s\\n\",str);
++i;
}
return NULL;
}
int main(void)
{
pthread_t pth; // this is our thread identifier
int i = 0;
/* Create worker thread */
pthread_create(&pth,NULL,threadFunc,\"processing...\");
/* wait for our thread to finish before continuing */
pthread_join(pth, NULL /* void ** return value could go here */);
while(i < 10 )
{
usleep(1);
printf(\"main() is running...\\n\");
++i;
}
return 0;
}
threadFunc()
, and then a bunch from
main()
.");
newEntry("Multithreading — Example Source",
"Here's some example code to illustrate thread creation:
#include <pthread.h>
#include <stdio.h>
/* This is our thread function. It is like main(), but for a thread*/
void *threadFunc(void *arg)
{
char *str;
int i = 0;
str=(char*)arg;
while(i < 110 )
{
usleep(1);
printf(\"threadFunc says: %s\\n\",str);
++i;
}
return NULL;
}
int main(void)
{
pthread_t pth; // this is our thread identifier
int i = 0;
pthread_create(&pth,NULL,threadFunc,\"foo\");
while(i < 100)
{
usleep(1);
printf(\"main is running...\\n\");
++i;
}
printf(\"main waiting for thread to terminate...\\n\");
pthread_join(pth,NULL);
return 0;
}
main()
and threadFunc()
threads execute and pause. Without the usleep()
's they'll not switch because we
aren't doing anything that takes long enough to consume our whole time slice.pthread_join()
call if we used a variable
instead of NULL
for the second argument.");
newEntry("Multithreading Terms",
"There are many terms used when writing multithreaded applications. I'll try to describe a few of there
here.Deadlock
— A state where two or more threads each hold a lock that the others need to finish.
For example, if one thread has locked mutex A and needs to lock mutex B to finish, while another thread is holding
mutex B and is waiting for mutex A to be released, they are in a state of
deadlock. The threads are stuck, and cannot finish. One way to avoid
deadlock is to acquire necessary mutexes in the same order (always get mutex A then
B). Another is to see if a mutex is available via
pthread_mutex_trylock
, and release any held locks if
one isn't available.Race Condition
— A program that depends on threads working in a certain sequence to complete
normally. Race Conditions happen when mutexes are used improperly, or not
at all.Thread-Safe
— A library that is designed to be used in multithreaded applications is said to be
thread-safe. If a library is not thread-safe, then one and only
one thread should make calls to that library's functions.