Chinaunix首页 | 论坛 | 博客
  • 博客访问: 355537
  • 博文数量: 78
  • 博客积分: 2222
  • 博客等级: 大尉
  • 技术积分: 745
  • 用 户 组: 普通用户
  • 注册时间: 2010-09-24 10:48
文章分类

全部博文(78)

文章存档

2012年(7)

2011年(33)

2010年(38)

分类: LINUX

2010-11-30 15:59:23

Thread Management

Creating and Terminating Threads

 Routines:

     (thread,attr,start_routine,arg)

     (status)

     (attr)

     (attr)

 Creating Threads:

  • Initially, your main() program comprises a single, default thread. All other threads must be explicitly created by the programmer.

  • pthread_create creates a new thread and makes it executable. This routine can be called any number of times from anywhere within your code.

  • pthread_create arguments:
    • thread: An opaque, unique identifier for the new thread returned by the subroutine.
    • attr: An opaque attribute object that may be used to set thread attributes. You can specify a thread attributes object, or NULL for the default values.
    • start_routine: the C routine that the thread will execute once it is created.
    • arg: A single argument that may be passed to start_routine. It must be passed by reference as a pointer cast of type void. NULL may be used if no argument is to be passed.

  • The maximum number of threads that may be created by a process is implementation dependent.

  • Once created, threads are peers, and may create other threads. There is no implied hierarchy or dependency between threads.

    Peer Threads

Question: After a thread has been created, how do you know when it will be scheduled to run by the operating system? 

 Thread Attributes:

  • By default, a thread is created with certain attributes. Some of these attributes can be changed by the programmer via the thread attribute object.

  • pthread_attr_init and pthread_attr_destroy are used to initialize/destroy the thread attribute object.

  • Other routines are then used to query/set specific attributes in the thread attribute object.

  • Some of these attributes will be discussed later.

 Terminating Threads:

  • There are several ways in which a Pthread may be terminated:
    • The thread returns from its starting routine (the main routine for the initial thread).
    • The thread makes a call to the pthread_exit subroutine (covered below).
    • The thread is canceled by another thread via the pthread_cancel routine (not covered here).
    • The entire process is terminated due to a call to either the exec or exit subroutines.

  • pthread_exit is used to explicitly exit a thread. Typically, the pthread_exit() routine is called after a thread has completed its work and is no longer required to exist.

  • If main() finishes before the threads it has created, and exits with pthread_exit(), the other threads will continue to execute. Otherwise, they will be automatically terminated when main() finishes.

  • The programmer may optionally specify a termination status, which is stored as a void pointer for any thread that may join the calling thread.

  • Cleanup: the pthread_exit() routine does not close files; any files opened inside the thread will remain open after the thread is terminated.

  • Discussion: In subroutines that execute to completion normally, you can often dispense with calling pthread_exit() - unless, of course, you want to pass a return code back. However, in main(), there is a definite problem if main() completes before the threads it spawned. If you don't call pthread_exit()explicitly, when main() completes, the process (and all threads) will be terminated. By calling pthread_exit() in main(), the process and all of its threads will be kept alive even though all of the code in main() has been executed.


Example: Pthread Creation and Termination

  • This simple example code creates 5 threads with the pthread_create() routine. Each thread prints a "Hello World!" message, and then terminates with a call to pthread_exit().

     Example Code - Pthread Creation and Termination

    #include 
    #include 
    #define NUM_THREADS     5
    
    void *PrintHello(void *threadid)
    {
       long tid;
       tid = (long)threadid;
       printf("Hello World! It's me, thread #%ld!\n", tid);
       pthread_exit(NULL);
    }
    
    int main (int argc, char *argv[])
    {
       pthread_t threads[NUM_THREADS];
       int rc;
       long t;
       for(t=0; trc = pthread_create(&threads[t], NULL, PrintHello, (void *)t);
          if (rc){
             printf("ERROR; return code from pthread_create() is %d\n", rc);
             exit(-1);
          }
       }
       pthread_exit(NULL);
    }
    
    View source code View sample output

Thread Management

Passing Arguments to Threads

  • The pthread_create() routine permits the programmer to pass one argument to the thread start routine. For cases where multiple arguments must be passed, this limitation is easily overcome by creating a structure which contains all of the arguments, and then passing a pointer to that structure in thepthread_create() routine.

  • All arguments must be passed by reference and cast to (void *).

Question: How can you safely pass data to newly created threads, given their non-deterministic start-up and scheduling? 
     Example 1 - Thread Argument Passing
      This code fragment demonstrates how to pass a simple integer to each thread. The calling thread uses a unique data structure for each thread, insuring that each thread's argument remains intact throughout the program.

    long *taskids[NUM_THREADS];
    
    for(t=0; trc = pthread_create(&threads[t], NULL, PrintHello, (void *) taskids[t]);
       ...
    }
    
    View source code View sample output

     Example 2 - Thread Argument Passing
      This example shows how to setup/pass multiple arguments via a structure. Each thread receives a unique instance of the structure.

    struct thread_data{
       int  thread_id;
       int  sum;
       char *message;
    };
    
    struct thread_data thread_data_array[NUM_THREADS];
    
    void *PrintHello(void *threadarg)
    {
       struct thread_data *my_data;
       ...
       my_data = (struct thread_data *) threadarg;
       taskid = my_data->thread_id;
       sum = my_data->sum;
       hello_msg = my_data->message;
       ...
    }
    
    int main (int argc, char *argv[])
    {
       ...
       thread_data_array[t].thread_id = t;
       thread_data_array[t].sum = sum;
       thread_data_array[t].message = messages[t];
       rc = pthread_create(&threads[t], NULL, PrintHello, 
            (void *) &thread_data_array[t]);
       ...
    }
    
    View source code View sample output

     Example 3 - Thread Argument Passing (Incorrect)
      This example performs argument passing incorrectly. It passes the address of variable t, which is shared memory space and visible to all threads. As the loop iterates, the value of this memory location changes, possibly before the created threads can access it.

    int rc;
    long t;
    
    for(t=0; trc = pthread_create(&threads[t], NULL, PrintHello, (void *) &t);
       ...
    }
    
    View source code View sample output

Thread Management

Joining and Detaching Threads

 Routines:

     (threadid,status)

     (threadid)

     (attr,detachstate)

     (attr,detachstate)

 Joining:

  • "Joining" is one way to accomplish synchronization between threads. For example:

    Joining

  • The pthread_join() subroutine blocks the calling thread until the specified threadid thread terminates.

  • The programmer is able to obtain the target thread's termination return status if it was specified in the target thread's call to pthread_exit().

  • A joining thread can match one pthread_join() call. It is a logical error to attempt multiple joins on the same thread.

  • Two other synchronization methods, mutexes and condition variables, will be discussed later.

 Joinable or Not?

  • When a thread is created, one of its attributes defines whether it is joinable or detached. Only threads that are created as joinable can be joined. If a thread is created as detached, it can never be joined.

  • The final draft of the POSIX standard specifies that threads should be created as joinable.

  • To explicitly create a thread as joinable or detached, the attr argument in the pthread_create() routine is used. The typical 4 step process is:
    1. Declare a pthread attribute variable of the pthread_attr_t data type
    2. Initialize the attribute variable with pthread_attr_init()
    3. Set the attribute detached status with pthread_attr_setdetachstate()
    4. When done, free library resources used by the attribute with pthread_attr_destroy()
 Detaching:

  • The pthread_detach() routine can be used to explicitly detach a thread even though it was created as joinable.

  • There is no converse routine.

 Recommendations:

  • If a thread requires joining, consider explicitly creating it as joinable. This provides portability as not all implementations may create threads as joinable by default.

  • If you know in advance that a thread will never need to join with another thread, consider creating it in a detached state. Some system resources may be able to be freed.


Example: Pthread Joining

     Example Code - Pthread Joining
      This example demonstrates how to "wait" for thread completions by using the Pthread join routine. Since some implementations of Pthreads may not create threads in a joinable state, the threads in this example are explicitly created in a joinable state so that they can be joined later.

    #include 
    #include 
    #include 
    #include 
    #define NUM_THREADS	4
    
    void *BusyWork(void *t)
    {
       int i;
       long tid;
       double result=0.0;
       tid = (long)t;
       printf("Thread %ld starting...\n",tid);
       for (i=0; i<1000000; i++)
       {
          result = result + sin(i) * tan(i);
       }
       printf("Thread %ld done. Result = %e\n",tid, result);
       pthread_exit((void*) t);
    }
    
    int main (int argc, char *argv[])
    {
       pthread_t thread[NUM_THREADS];
       pthread_attr_t attr;
       int rc;
       long t;
       void *status;
    
       /* Initialize and set thread detached attribute */
       pthread_attr_init(&attr);
       pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
    
       for(t=0; trc = pthread_create(&thread[t], &attr, BusyWork, (void *)t); 
          if (rc) {
             printf("ERROR; return code from pthread_create() 
                    is %d\n", rc);
             exit(-1);
             }
          }
    
       /* Free attribute and wait for the other threads */
       pthread_attr_destroy(&attr);
       for(t=0; trc = pthread_join(thread[t], &status);
          if (rc) {
             printf("ERROR; return code from pthread_join() 
                    is %d\n", rc);
             exit(-1);
             }
          printf("Main: completed join with thread %ld having a status   
                of %ld\n",t,(long)status);
          }
     
    printf("Main: program completed. Exiting.\n");
    pthread_exit(NULL);
    }
    
    View source code View sample output

Thread Management

Stack Management

 Routines:

     (attr, stacksize)

     (attr, stacksize)

     (attr, stackaddr)

     (attr, stackaddr)

 Preventing Stack Problems:

  • The POSIX standard does not dictate the size of a thread's stack. This is implementation dependent and varies.

  • Exceeding the default stack limit is often very easy to do, with the usual results: program termination and/or corrupted data.

  • Safe and portable programs do not depend upon the default stack limit, but instead, explicitly allocate enough stack for each thread by using thepthread_attr_setstacksize routine.

  • The pthread_attr_getstackaddr and pthread_attr_setstackaddr routines can be used by applications in an environment where the stack for a thread must be placed in some particular region of memory.

 Some Practical Examples at LC:

  • Default thread stack size varies greatly. The maximum size that can be obtained also varies greatly, and may depend upon the number of threads per node.

    Node
    Architecture
    #CPUsMemory (GB)Default Size
    (bytes)
    AMD Opteron8162,097,152
    Intel IA644833,554,432
    Intel IA32242,097,152
    IBM Power5832196,608
    IBM Power4816196,608
    IBM Power3161698,304


Example: Stack Management

     Example Code - Stack Management
      This example demonstrates how to query and set a thread's stack size.

    #include 
    #include 
    #define NTHREADS 4
    #define N 1000
    #define MEGEXTRA 1000000
     
    pthread_attr_t attr;
     
    void *dowork(void *threadid)
    {
       double A[N][N];
       int i,j;
       long tid;
       size_t mystacksize;
    
       tid = (long)threadid;
       pthread_attr_getstacksize (&attr, &mystacksize);
       printf("Thread %ld: stack size = %li bytes \n", tid, mystacksize);
       for (i=0; ipthread_exit(NULL);
    }
     
    int main(int argc, char *argv[])
    {
       pthread_t threads[NTHREADS];
       size_t stacksize;
       int rc;
       long t;
     
       pthread_attr_init(&attr);
       pthread_attr_getstacksize (&attr, &stacksize);
       printf("Default stack size = %li\n", stacksize);
       stacksize = sizeof(double)*N*N+MEGEXTRA;
       printf("Amount of stack needed per thread = %li\n",stacksize);
       pthread_attr_setstacksize (&attr, stacksize);
       printf("Creating threads with stack size = %li bytes\n",stacksize);
       for(t=0; tpthread_create(&threads[t], &attr, dowork, (void *)t);
          if (rc){
             printf("ERROR; return code from pthread_create() is %d\n", rc);
             exit(-1);
          }
       }
       printf("Created %ld threads.\n", t);
       pthread_exit(NULL);
    }
    

Thread Management

Miscellaneous Routines

     ()

     (thread1,thread2)

  • pthread_self returns the unique, system assigned thread ID of the calling thread.

  • pthread_equal compares two thread IDs. If the two IDs are different 0 is returned, otherwise a non-zero value is returned.

  • Note that for both of these routines, the thread identifier objects are opaque and can not be easily inspected. Because thread IDs are opaque objects, the C language equivalence operator == should not be used to compare two thread IDs against each other, or to compare a single thread ID against another value.

     (once_control, init_routine)

  • pthread_once executes the init_routine exactly once in a process. The first call to this routine by any thread in the process executes the giveninit_routine, without parameters. Any subsequent call will have no effect.

  • The init_routine routine is typically an initialization routine.

  • The once_control parameter is a synchronization control structure that requires initialization prior to calling pthread_once. For example:

    pthread_once_t once_control = PTHREAD_ONCE_INIT;


阅读(1281) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~