没有合适的资源?快使用搜索试试~ 我知道了~
资源推荐
资源详情
资源评论
Multi-Threaded Programming With POSIX Threads
[LUPG Home] [Tutorials] [Related Material] [Essays] [Project Ideas] [Send Comments]
v1.2
Multi-Threaded Programming With POSIX
Threads
Table Of Contents:
1.
Before We Start...
2.
What Is a Thread? Why Use Threads?
3.
Creating And Destroying Threads
4.
Synchronizing Threads With Mutexes
1.
What Is A Mutex?
2.
Creating And Initializing A Mutex
3.
Locking And Unlocking A Mutex
4.
Destroying A Mutex
5.
Using A Mutex - A Complete Example
6.
Starvation And Deadlock Situations
5.
Refined Synchronization - Condition Variables
1.
What Is A Condition Variable?
2.
Creating And Initializing A Condition Variable
3.
Signaling A Condition Variable
4.
Waiting On A Condition Variable
5.
Destroying A Condition Variable
6.
A Real Condition For A Condition Variable
7.
Using A Condition Variable - A Complete Example
6.
"Private" thread data - Thread-Specific Data
1.
Overview Of Thread-Specific Data Support
2.
Allocating Thread-Specific Data Block
3.
Accessing Thread-Specific Data
4.
Deleting Thread-Specific Data Block
5.
A Complete Example
7.
Thread Cancellation And Termination
1.
Canceling A Thread
2.
Setting Thread Cancellation State
3.
Cancellation Points
4.
Setting Thread Cleanup Functions
5.
Synchronizing On Threads Exiting
6.
Detaching A Thread
7.
Threads Cancellation - A Complete Example
8.
Using Threads For Responsive User Interface Programming
1.
User Interaction - A Complete Example
9.
Using 3rd-Party Libraries In A Multi-Threaded Application
10.
Using A Threads-Aware Debugger
http://users.actcom.co.il/~choo/lupg/tutorials/multi-thread/multi-thread.html (1 of 22) [01/04/2003 09:52:59 a.m.]
Multi-Threaded Programming With POSIX Threads
Before We Start...
This tutorial is an attempt to help you become familiar with multi-threaded programming with the POSIX threads
(pthreads) library, and attempts to show how its features can be used in "real-life" programs. It explains the different
tools defined by the library, shows how to use them, and then gives an example of using them to solve programming
problems. There is an implicit assumption that the user has some theoretical familiarity with paralell programming (or
multi-processing) concepts. Users without such background might find the concepts harder to grasp. A seperate tutorial
will be prepared to explain the theoreticl background and terms to those who are familiar only with normal "serial"
programming.
I would assume that users which are familiar with asynchronous programming models, such as those used in
windowing environments (X, Motif), will find it easier to grasp the concepts of multi-threaded programming.
When talking about POSIX threads, one cannot avoid the question "Which draft of the POSIX threads standard shall be
used?". As this threads standard has been revised over a period of several years, one will find that implementations
adhering to different drafts of the standard have a different set of functions, different default values, and different
nuances. Since this tutorial was written using a Linux system with the kernel-level LinuxThreads library, v0.5,
programmers with access to other systems, using different versions of pthreads, should refer to their system's manuals
in case of incompatibilities. Also, since some of the example programs are using blocking system calls, they won't
work with user-level threading libraries (refer to our
parallel programming theory tutorial for more information).
Having said that, i'd try to check the example programs on other systems as well (Solaris 2.5 comes to mind), to make it
more "cross-platform".
What Is a Thread? Why Use Threads
A thread is a semi-process, that has its own stack, and executes a given piece of code. Unlike a real process, the thread
normally shares its memory with other threads (where as for processes we usually have a different memory area for
each one of them). A Thread Group is a set of threads all executing inside the same process. They all share the same
memory, and thus can access the same global variables, same heap memory, same set of file descriptors, etc. All these
threads execute in parallel (i.e. using time slices, or if the system has several processors, then really in parallel).
The advantage of using a thread group instead of a normal serial program is that several operations may be carried out
in parallel, and thus events can be handled immediately as they arrive (for example, if we have one thread handling a
user interface, and another thread handling database queries, we can execute a heavy query requested by the user, and
still respond to user input while the query is executed).
The advantage of using a thread group over using a process group is that context switching between threads is much
faster than context switching between processes (context switching means that the system switches from running one
thread or process, to running another thread or process). Also, communications between two threads is usually faster
and easier to implement than communications between two processes.
On the other hand, because threads in a group all use the same memory space, if one of them corrupts the contents of its
memory, other threads might suffer as well. With processes, the operating system normally protects processes from one
another, and thus if one corrupts its own memory space, other processes won't suffer. Another advantage of using
processes is that they can run on different machines, while all the threads have to run on the same machine (at least
normally).
Creating And Destroying Threads
http://users.actcom.co.il/~choo/lupg/tutorials/multi-thread/multi-thread.html (2 of 22) [01/04/2003 09:52:59 a.m.]
Multi-Threaded Programming With POSIX Threads
When a multi-threaded program starts executing, it has one thread running, which executes the main() function of the
program. This is already a full-fledged thread, with its own thread ID. In order to create a new thread, the program
should use the pthread_create() function. Here is how to use it:
#include <stdio.h> /* standard I/O routines */
#include <pthread.h> /* pthread functions and data structures */
/* function to be executed by the new thread */
void*
do_loop(void* data)
{
int i; /* counter, to print numbers */
int j; /* counter, for delay */
int me = *((int*)data); /* thread identifying number */
for (i=0; i<10; i++) {
for (j=0; j<500000; j++) /* delay loop */
;
printf("'%d' - Got '%d'\n", me, i);
}
/* terminate the thread */
pthread_exit(NULL);
}
/* like any C program, program's execution begins in main */
int
main(int argc, char* argv[])
{
int thr_id; /* thread ID for the newly created thread */
pthread_t p_thread; /* thread's structure */
int a = 1; /* thread 1 identifying number */
int b = 2; /* thread 2 identifying number */
/* create a new thread that will execute 'do_loop()' */
thr_id = pthread_create(&p_thread, NULL, do_loop, (void*)&a);
/* run 'do_loop()' in the main thread as well */
do_loop((void*)&b);
/* NOT REACHED */
return 0;
}
A few notes should be mentioned about this program:
1. Note that the main program is also a thread, so it executes the do_loop() function in parallel to the thread it
creates.
2. pthread_create() gets 4 parameters. The first parameter is used by pthread_create() to supply the
program with information about the thread. The second parameter is used to set some attributes for the new
thread. In our case we supplied a NULL pointer to tell pthread_create() to use the default values. The
third parameter is the name of the function that the thread will start executing. The forth parameter is an
argument to pass to this function. Note the cast to a 'void*'. It is not required by ANSI-C syntax, but is placed
here for clarification.
3. The delay loop inside the function is used only to demonstrate that the threads are executing in parallel. Use a
http://users.actcom.co.il/~choo/lupg/tutorials/multi-thread/multi-thread.html (3 of 22) [01/04/2003 09:52:59 a.m.]
Multi-Threaded Programming With POSIX Threads
larger delay value if your CPU runs too fast, and you see all the printouts of one thread before the other.
4.
The call to pthread_exit() Causes the current thread to exit and free any thread-specific resources it is
taking. There is no need to use this call at the end of the thread's top function, since when it returns, the thread
would exit automatically anyway. This function is useful if we want to exit a thread in the middle of its
execution.
In order to compile a multi-threaded program using gcc, we need to link it with the pthreads library. Assuming you
have this library already installed on your system, here is how to compile our first program:
gcc pthread_create.c -o pthread_create -lpthread
Note that for some of the programs later on this tutorial, one may need to add a '-D_GNU_SOURCE' flag to this
compile line, to get the source compiled.
The source code for this program may be found in the
pthread_create.c file.
Synchronizing Threads With Mutexes
One of the basic problems when running several threads that use the same memory space, is making sure they don't
"step on each other's toes". By this we refer to the problem of using a data structure from two different threads.
For instance, consider the case where two threads try to update two variables. One tries to set both to 0, and the other
tries to set both to 1. If both threads would try to do that at the same time, we might get with a situation where one
variable contains 1, and one contains 0. This is because a context-switch (we already know what this is by now, right?)
might occur after the first tread zeroed out the first variable, then the second thread would set both variables to 1, and
when the first thread resumes operation, it will zero out the second variable, thus getting the first variable set to '1', and
the second set to '0'.
What Is A Mutex?
A basic mechanism supplied by the pthreads library to solve this problem, is called a mutex. A mutex is a lock that
guarantees three things:
1.
Atomicity - Locking a mutex is an atomic operation, meaning that the operating system (or threads library)
assures you that if you locked a mutex, no other thread succeeded in locking this mutex at the same time.
2.
Singularity - If a thread managed to lock a mutex, it is assured that no other thread will be able to lock the thread
until the original thread releases the lock.
3.
Non-Busy Wait - If a thread attempts to lock a thread that was locked by a second thread, the first thread will be
suspended (and will not consume any CPU resources) until the lock is freed by the second thread. At this time,
the first thread will wake up and continue execution, having the mutex locked by it.
From these three points we can see how a mutex can be used to assure exclusive access to variables (or in general
critical code sections). Here is some pseudo-code that updates the two variables we were talking about in the previous
section, and can be used by the first thread:
lock mutex 'X1'.
set first variable to '0'.
set second variable to '0'.
unlock mutex 'X1'.
http://users.actcom.co.il/~choo/lupg/tutorials/multi-thread/multi-thread.html (4 of 22) [01/04/2003 09:52:59 a.m.]
Multi-Threaded Programming With POSIX Threads
Meanwhile, the second thread will do something like this:
lock mutex 'X1'.
set first variable to '1'.
set second variable to '1'.
unlock mutex 'X1'.
Assuming both threads use the same mutex, we are assured that after they both ran through this code, either both
variables are set to '0', or both are set to '1'. You'd note this requires some work from the programmer - If a third thread
was to access these variables via some code that does not use this mutex, it still might mess up the variable's contents.
Thus, it is important to enclose all the code that accesses these variables in a small set of functions, and always use only
these functions to access these variables.
Creating And Initializing A Mutex
In order to create a mutex, we first need to declare a variable of type pthread_mutex_t, and then initialize it. The
simplest way it by assigning it the PTHREAD_MUTEX_INITIALIZER constant. So we'll use a code that looks
something like this:
pthread_mutex_t a_mutex = PTHREAD_MUTEX_INITIALIZER;
One note should be made here: This type of initialization creates a mutex called 'fast mutex'. This means that if a thread
locks the mutex and then tries to lock it again, it'll get stuck - it will be in a deadlock.
There is another type of mutex, called 'recursive mutex', which allows the thread that locked it, to lock it several more
times, without getting blocked (but other threads that try to lock the mutex now will get blocked). If the thread then
unlocks the mutex, it'll still be locked, until it is unlocked the same amount of times as it was locked. This is similar to
the way modern door locks work - if you turned it twice clockwise to lock it, you need to turn it twice counter-
clockwise to unlock it. This kind of mutex can be created by assigning the constant
PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP to a mutex variable.
Locking And Unlocking A Mutex
In order to lock a mutex, we may use the function pthread_mutex_lock(). This function attempts to lock the
mutex, or block the thread if the mutex is already locked by another thread. In this case, when the mutex is unlocked by
the first process, the function will return with the mutex locked by our process. Here is how to lock a mutex (assuming
it was initialized earlier):
int rc = pthread_mutex_lock(&a_mutex);
if (rc) { /* an error has occurred */
perror("pthread_mutex_lock");
pthread_exit(NULL);
}
/* mutex is now locked - do your stuff. */
.
.
http://users.actcom.co.il/~choo/lupg/tutorials/multi-thread/multi-thread.html (5 of 22) [01/04/2003 09:52:59 a.m.]
剩余55页未读,继续阅读
资源评论
xhxzzh1
- 粉丝: 0
- 资源: 1
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功