标签:
This chapter briefly covers the main properties of the thread API. Each part will be explained
further in the subsequent chapters, as we know how to use the API. More details can be found
in various books and online sources. We should note that the subsequent chapters introduces
the concepts of locks and condition variables more slowly, with many examples; this chpater is
thus better used as a reference.
Crux: How to Create and Control Threads
What interface should the OS present for thread creation and control ? How should these
interfaces be designed to enable ease use of as well as utility?
Thread Creation
The first thing you have to be aboe to write a multi-threaded program is to create new threads,
and thus some kind of thread creation interface must exist. In POSIX, it is easy:
#include <pthread.h> int pthread_create(pthread_t* thread, const pthread_attr_t* attr. void* (*start_routine)(void*), void* arg);
This declaration might look a little complex (particularly if you haven‘t used function pointers in
C), but actually it‘s not too bad. There are four arguments: thread, attr, start_routines, and arg.
The first, thread, is a pointer to a structure of type pthread_t; we will use this structure to
interact with this thread, and thus we need to pass it to pthread_create() in order to initialize it.
The second argument, attr, is used to specify any attributes this thread might have. Some
example include setting the stack size or perhaps information about the scheduling priority of
the thread. An attribute is initialized with a separate call to pthread_attr_init(); see the mannual
page for details. However, in most cases, the defaults will be fine; in this case, we will simply
pass the value NULL in.
The third argument is the most complex, but is really just asking: which function should this
thread start running in? In C, we call this a function pointer, and this one tells us the following
is expected: a function name (start_routine), which is passed a single argument of type void*
(as indicated in the parenthese after start_routine), and which returns a valur of type void*.
If this routine instead required an integer argument, instead of a void pointer, the declaration
would look like this:
int pthread_create(..., void* (*start_routine)(int), int arg);
If instead the routine took a void pointer as an argument, but returned an integer, it would look
look this:
int pthread_create(..., int (*start_routine)(void*), void* arg);
Finally, the fouth argument, arg, is exactly the argument to be passed to the function where
the thread begins execution. You might ask: why do we need these void pointers? Well, the
answer is quite simple: having a void pointer as an argument to the function start_routine allows
us to pass in any type of argument; having it as a return value allows the thread to return any
type of result.
Let‘s look at an example. Here we just create a thread that is passed two arguments, packaged
into a single type we define ourselves. The thread, once created, can simply cast its argument to
the type it expects and thus unpack the arguments as desired.
And there it is! Once you create a thread, you really ahve another live executing entity, complete
with its own call stack, running within the same address space as all the currently existing
threads in the program. The fun thus begins!
#include <pthread.h> typedef struct __myarg_t { int a; int b; } myarg_t; void* mythread(void* arg) { myarg_t* m = (myarg_t*)arg; printf("%d %d\n", m->a, m->b); return NULL; } int main(int argc, char* argv[]) { pthread_t p; int rc; myarg_t args; args.a = 10; args.b = 20; rc = pthread_create(&p, NULL, mythread, &arg); ...... }
Thread Completion
The example above shows how to create a thread. However, what happens if you want to wait
for a thread to complete? You need to do something special in order to wait for completion;
in particular, you must call the routine pthread_join().
int pthread_join(pthread_t thread, void** value_ptr);
This routine takses two arguments. The first is of type pthread_t, and is used to specify which
thread to wait for. This variable is initialized by the thread creation routine (when you pass a
pointer to it as an argument to pthread_create()); if you keep it around, you can use it to wait
for that thread to terminate.
The second argument is pointer to the return value you expect to get back. Because the routine
can return anything, it is defined to return a pointer to void; because the pthread_join() routine
changes the value of the passed in argument, you need to pass in a pointer to that value, not
just the value itself.
Let‘s look at another example. In the code, a single thread is again created, and passed a couple
of arguments via the myarg_t structure. To return values, the myret_t type is used. Once the
thread is finished running, the main thread, which has been waiting inside of the pthread_join()
routine, then returns, and we can access the values returned from the thread, namely is in
myarg_t.
Second, if we are just passing in a single value (e.g., an int), we don‘t have to package it up
as an argument. Figure 27.3 shows an example. In this case, life is a bit simpler, as we don not
have to package arguments and return values inside of functions.
Third, we should note that one has to be extremely careful with how values are returned from
a thread. In particular, never return a pointer which refers to something allocated on the thread‘s
call stack. If you do, what do you think will happen? (think about it!) Here is an example of a
dangerous piece of code, modified from the exampe in Figure 27.2.
void* mythread(void* arg) { myarg_t* m = (myarg_t*)arg; printf("%d %d\n", m->a, m->b); myret_t r; r.x = 1; r.y = 2; return (void*)&r; }
THIS IS PLACEHOLDER.
Operating System: Three Easy Pieces --- Thread API (Note)
标签:
原文地址:http://www.cnblogs.com/miaoyong/p/4966134.html