Thread Management线程管理 |
Passing Arguments to Threads线程的参数传递
- pthread_create()例程仅仅允许开发者传递一个参数到需要启动的线程。对于需要传递多个参数的情况,这种限制可以通过构造一个包含所有参数的结构,并把结构的指针作为参数传递到pthread_create(),从而绕过这个限制。
- 所有的参数必须利用(void *)来传递。
Example 1 - 线程参数传递 这段代码展示了怎样传递一个简单的整数到需要启动的线程中。调用的线程使用一个唯一的数据结构以确保每一个线程的参数在整个过程中保持完整。 /******************************************************************************
* FILE: hello_arg1.c
* DESCRIPTION:
* A "hello world" Pthreads program which demonstrates one safe way
* to pass arguments to threads during thread creation.
* AUTHOR: Blaise Barney
* LAST REVISED: 04/05/05
******************************************************************************/
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#define NUM_THREADS 8
char *messages[NUM_THREADS];
void *PrintHello(void *threadid)
{
int *id_ptr, taskid;
sleep(1);
id_ptr = (int *) threadid;
taskid = *id_ptr;
printf("Thread %d: %s/n", taskid, messages[taskid]);
pthread_exit(NULL);
}
int main(int argc, char *argv[])
{
pthread_t threads[NUM_THREADS];
int *taskids[NUM_THREADS];
int rc, t;
messages[0] = "English: Hello World!";
messages[1] = "French: Bonjour, le monde!";
messages[2] = "Spanish: Hola al mundo";
messages[3] = "Klingon: Nuq neH!";
messages[4] = "German: Guten Tag, Welt!";
messages[5] = "Russian: Zdravstvytye, mir!";
messages[6] = "Japan: Sekai e konnichiwa!";
messages[7] = "Latin: Orbis, te saluto!";
for(t=0;t<NUM_THREADS;t++) {
taskids[t] = (int *) malloc(sizeof(int));
*taskids[t] = t;
printf("Creating thread %d/n", t);
rc = pthread_create(&threads[t], NULL, PrintHello, (void *) taskids[t]);
if (rc) {
printf("ERROR; return code from pthread_create() is %d/n", rc);
exit(-1);
}
}
pthread_exit(NULL);
} ―――――――――――――――――――――――――――――――――――――――― 输出:
Creating thread 0
Creating thread 1
Creating thread 2
Creating thread 3
Creating thread 4
Creating thread 5
Creating thread 6
Creating thread 7
Thread 0: English: Hello World!
Thread 1: French: Bonjour, le monde!
Thread 2: Spanish: Hola al mundo
Thread 3: Klingon: Nuq neH!
Thread 4: German: Guten Tag, Welt!
Thread 5: Russian: Zdravstvytye, mir!
Thread 6: Japan: Sekai e konnichiwa!
Thread 7: Latin: Orbis, te saluto!
|
Example 2 - 线程参数传递 本例展示通过一个结来设置/传递多个参数。每一个线程接收到此结构的唯一实例。 /******************************************************************************
* FILE: hello_arg2.c
* DESCRIPTION:
* A "hello world" Pthreads program which demonstrates another safe way
* to pass arguments to threads during thread creation. In this case,
* a structure is used to pass multiple arguments.
* AUTHOR: Blaise Barney
* LAST REVISED: 04/05/05
******************************************************************************/
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#define NUM_THREADS 8
char *messages[NUM_THREADS];
struct thread_data
{
int thread_id;
int sum;
char *message;
};
struct thread_data thread_data_array[NUM_THREADS];
void *PrintHello(void *threadarg)
{
int taskid, sum;
char *hello_msg;
struct thread_data *my_data;
sleep(1);
my_data = (struct thread_data *) threadarg;
taskid = my_data->thread_id;
sum = my_data->sum;
hello_msg = my_data->message;
printf("Thread %d: %s Sum=%d/n", taskid, hello_msg, sum);
pthread_exit(NULL);
}
int main(int argc, char *argv[])
{
pthread_t threads[NUM_THREADS];
int *taskids[NUM_THREADS];
int rc, t, sum;
sum=0;
messages[0] = "English: Hello World!";
messages[1] = "French: Bonjour, le monde!";
messages[2] = "Spanish: Hola al mundo";
messages[3] = "Klingon: Nuq neH!";
messages[4] = "German: Guten Tag, Welt!";
messages[5] = "Russian: Zdravstvytye, mir!";
messages[6] = "Japan: Sekai e konnichiwa!";
messages[7] = "Latin: Orbis, te saluto!";
for(t=0;t<NUM_THREADS;t++) {
sum = sum + t;
thread_data_array[t].thread_id = t;
thread_data_array[t].sum = sum;
thread_data_array[t].message = messages[t];
printf("Creating thread %d/n", t);
rc = pthread_create(&threads[t], NULL, PrintHello, (void *)
&thread_data_array[t]);
if (rc) {
printf("ERROR; return code from pthread_create() is %d/n", rc);
exit(-1);
}
}
pthread_exit(NULL);
}
―――――――――――――――――――――――――――――――――――――――― 输出: Creating thread 0
Creating thread 1
Creating thread 2
Creating thread 3
Creating thread 4
Creating thread 5
Creating thread 6
Creating thread 7
Thread 0: English: Hello World! Sum=0
Thread 1: French: Bonjour, le monde! Sum=1
Thread 2: Spanish: Hola al mundo Sum=3
Thread 3: Klingon: Nuq neH! Sum=6
Thread 4: German: Guten Tag, Welt! Sum=10 Thread 5: Russian: Zdravstvytye, mir! Sum=15 Thread 6: Japan: Sekai e konnichiwa! Sum=21 Thread 7: Latin: Orbis, te saluto! Sum=28 |
Example 3 -线程参数传递(错误的) 本例展示了错误的参事传递。创建线程的循环在获取它之前已经修改了作为被传递参数的地址。 /*****************************************************************************
* FILE: hello_arg3.c
* DESCRIPTION:
* This "hello world" Pthreads program demonstrates an unsafe (incorrect)
* way to pass thread arguments at thread creation. In this case, the
* argument variable is changed by the main thread as it creates new threads.
* AUTHOR: Blaise Barney
* LAST REVISED: 06/27/07
******************************************************************************/
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#define NUM_THREADS 8
void *PrintHello(void *threadid)
{
int *id_ptr, taskid;
sleep(1);
id_ptr = (int *) threadid;
taskid = *id_ptr;
printf("Hello from thread %d/n", taskid);
pthread_exit(NULL);
}
int main(int argc, char *argv[])
{
pthread_t threads[NUM_THREADS];
int rc, t;
for(t=0;t<NUM_THREADS;t++) {
printf("Creating thread %d/n", t);
rc = 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);
}
――――――――――――――――――――――――――――――――――――――― 输出: Creating thread 0
Creating thread 1
Creating thread 2
Creating thread 3
Creating thread 4
Creating thread 5
Creating thread 6
Creating thread 7
Hello from thread 8
Hello from thread 8
Hello from thread 8
Hello from thread 8
Hello from thread 8
Hello from thread 8
Hello from thread 8
Hello from thread 8 |
线程管理 |
合并和分离线程
Routines:
pthread_join (threadid,status) pthread_detach (threadid,status) pthread_attr_setdetachstate (attr,detachstate) pthread_attr_getdetachstate (attr,detachstate) |
合并(Joining):
- 合并("Joining")是线程间实现同步的一种方式,例如:
- pthread_join()会阻塞调用线程直到被调用线程完成。
- 如果在目标线程中调用了pthread_exit(),开发者在线程返回时可以获得目标线程的返回状态。
- 一个可合并的线程与一个pthread_join()调用一样。多个线程尝试合并到同一线程在逻辑上就是错误的。
- 其他的2个同步方法:互斥和条件变量,将在后面讨论。.
能否合并?
- 当线程创建时,其中有一个属性就是“是否允许合并”。仅仅在其属性是可以合并时,线程才允许合并。如果是用分离的属性创建线程时,此线程永远不能合并。
- POSIX标准的最后版本指出线程应该以允许合并的方式创建,然而,不是所有的实现都会符合此标准。
- 为了显式的以可合并或者可分离的方式创建线程,需要使用pthread_create()中的attr参数,具体分为以下典型的4步:
- 利用pthread_attr_t数据类型申请一个pthread 的attribute 变量。
- 利用pthread_attr_init()初始化此pthread attribute 变量 。
- 利用pthread_attr_setdetachstate()来设置attribute属性值。
- 当使用完成后,利用pthread_attr_destroy()来释放相关资源。
分离(Detaching):
- 不管线程是否以可合并的方式创建,都可以利用pthread_detach()来显式的分离线程。
- 但这里没有提供相反的接口(即把以分离方式创建的线程合并)。
建议:
- 如果线程需要合并,考虑以可合并的方式显式创建,这样可以避免以不可合并的属性作为缺省方式来创建线程,从而为代码提供较好的可移植性。
- 如果你事先知道一个线程一定不会与其他线程合并,考虑以分离的方式创建线程,这样可以节约一些资源。
Example: Pthread 合并
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. /*****************************************************************************
* FILE: join1.c
* DESCRIPTION:
* This example demonstrates how to "wait" for thread completions by using
* the Pthread join routine. Since not all current implementations of
* Pthreads 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.
* AUTHOR: 8/98 Blaise Barney
* LAST REVISED: 04/06/05
******************************************************************************/
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#define NUM_THREADS 3
void *BusyWork(void *null)
{
int i;
double result=0.0;
for (i=0; i<1000000; i++)
{
result = result + (double)random();
}
printf("Thread result = %e/n",result);
pthread_exit((void *) 0);
}
int main(int argc, char *argv[])
{
pthread_t thread[NUM_THREADS];
pthread_attr_t attr;
int rc, t;
void *status;
/* Initialize and set thread detached attribute */
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
for(t=0;t<NUM_THREADS;t++)
{
printf("Creating thread %d/n", t);
rc = pthread_create(&thread[t], &attr, BusyWork, NULL);
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;t<NUM_THREADS;t++)
{
rc = pthread_join(thread[t], &status);
if (rc)
{
printf("ERROR return code from pthread_join() is %d/n", rc);
exit(-1);
}
printf("Completed join with thread %d status= %ld/n",t,(long)status);
}
pthread_exit(NULL);
}
――――――――――――――――――――――――――――――――――――――― 输出:
Creating thread 0
Creating thread 1
Creating thread 2
Thread result = 1.073937e+15
Thread result = 1.073861e+15
Thread result = 1.073767e+15
Completed join with thread 0 status= 0
Completed join with thread 1 status= 0
Completed join with thread 2 status= 0 |
Thread Management线程管理 |
Stack Management栈管理
Routines:
pthread_attr_getstacksize (attr, stacksize) pthread_attr_setstacksize (attr, stacksize) pthread_attr_getstackaddr (attr, stackaddr) pthread_attr_setstackaddr (attr, stackaddr) |
预防栈问题:
- POSIX标准并没有规定线程栈的大小,它与不同架构的具体实现有关。
- POSIX用缺省的栈大小来实现很容易,通常的结果是:由于不同的实现会导致程序终止和/或者数据损坏。
- 安全和可移植的方式是不要依靠缺省的栈大小,而是用pthread_attr_setstacksize()来为每个线程显式的分配足够大小的栈。
- pthread_attr_getstackaddr() 和 pthread_attr_setstackaddr()可以在使栈在特定情况下也能被应用程序使用。
在LC中的一些实际范例:
- 缺省的线程栈大小变化很大,栈的大小根据每节点的线程数目和能够获取的内存大小而发生很大的变化。
Node | #CPUs | Memory (GB) | Default Size |
AMD Opteron | 8 | 16 | 2,097,152 |
Intel IA64 | 4 | 8 | 33,554,432 |
Intel IA32 | 2 | 4 | 2,097,152 |
IBM Power5 | 8 | 32 | 196,608 |
IBM Power4 | 8 | 16 | 196,608 |
IBM Power3 | 16 | 16 | 98,304 |
Example: 栈管理
Example Code - Stack Management This example demonstrates how to query and set a thread's stack size. #include <pthread.h> #include <stdio.h> #define NTHREADS 4 #define N 1000 #define MEGEXTRA 1000000
pthread_attr_t attr;
void *dowork(void *threadid) { double A[N][N]; int i,j,tid; size_t mystacksize;
tid = (int)threadid; pthread_attr_getstacksize (&attr, &mystacksize); printf("Thread %d: stack size = %li bytes /n", tid, mystacksize); for (i=0; i<N; i++) for (j=0; j<N; j++) A[i][j] = ((i*j)/3.452) + (N-i); pthread_exit(NULL); }
int main(int argc, char *argv[]) { pthread_t threads[NTHREADS]; size_t stacksize; int rc, 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; t<NTHREADS; t++){ rc = pthread_create(&threads[t], &attr, dowork, (void *)t); if (rc){ printf("ERROR; return code from pthread_create() is %d/n", rc); exit(-1); } } printf("Created %d threads./n", t); pthread_exit(NULL); } |
线程管理 |
Miscellaneous Routines
pthread_self () pthread_equal (thread1,thread2) |
- pthread_self()返回系统分配的,独一无二的线程ID。
- pthread_equal()比较两线程的线程IDs,如果两ID不同,返回0,否则返回非0。
- 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.
pthread_once (once_control, init_routine) |
- 在一个进程中,pthread_once仅仅执行一次init_routine(),即在同一进程中的线程第一次调用它时,会执行init_routine(),任何后续的调用将不再有任何影响。
- init_routin()e是一个典型的初始化例程。
- once_control参数是一同步控制结构,要求在调用pthread_once() 之前被初始化,例如:
pthread_once_t once_control = PTHREAD_ONCE_INIT;
- pthread_yield()强制调用线程放弃CPU,然后处于运行队列中直到被重新调度。