POSIX多线程编程(上)-基本概念
线程概念
我们把正在计算机中执行的程序叫做"进程"(Process) ,而不将其称为程序(Program)。所谓"线程"(Thread),是"进程"中某个单一顺序的控制流。线程是进程中的实体,一个进程可以拥有多个线程,一个线程必须有一个父进程。线程不拥有系统资源,只有运行必须的一些数据结构;它与父进程的其它线程共享该进程所拥有的全部资源。
在多中央处理器的系统里,不同线程可以同时在不同的中央处理器上运行,甚至当它们属于同一个进程时也是如此。大多数支持多处理器的操作系统都提供编程接口来让进程可以控制自己的线程与各处理器之间的关联度(affinity)。
有时候,线程也称作轻量级进程。就象进程一样,线程在程序中是独立的、并发的执行路径,每个线程有它自己的堆栈、自己的程序计数器和自己的局部变量。但是,与分隔的进程相比,进程中的线程之间的隔离程度要小。它们共享内存、文件句柄和其它每个进程应有的状态。
线程标识
类似于每一个进程都有唯一的进程ID(pid_t)标识,每一个线程也有对应的线程ID(pthread_t)标识。不同之处在于进程ID在整个系统中是唯一的而线程ID仅在它所属的进程环境中有效。与线程标识操作有关的函数接口如下:
#include <pthread.h>
int pthread_equal(pthread_t t1, pthread_t t2); // compare thread IDs
pthread_t pthread_self(void); // obtain ID of the calling thread
线程的创建、终止、取消
线程创建、终止
线程的创建可以通过调用pthread_create()接口创建,线程会采用以下四种方式终止:
-
It calls
pthread_exit(), specifying an exit status value that is available to another thread in the same process that callspthread_join(). -
It returns from start_routine(). This is equivalent to calling
pthread_exit()with the value supplied in the return statement. -
It is canceled (see
pthread_cancel()). -
Any of the threads in the process calls exit(), or the main thread performs a return from
main(). This causes the termination of all threads in the process.
函数原型:
#include <pthread.h>
/**
The attr argument points to a pthread_attr_t structure whose contents are used at thread creation time to determine attributes for the new thread; this structure is
initialized using pthread_attr_init(3) and related functions. If attr is NULL, then the thread is created with default attributes.
RETURN VALUE
On success, pthread_create() returns 0; on error, it returns an error number, and the contents of *thread are undefined.
*/
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
void pthread_exit(void *retval);
下面一段代码是linux函数手册一个demo很好的展示了线程的创建、终止、等待:
EXAMPLE
The program below demonstrates the use of pthread_create(), as well as a number of other functions in the pthreads API.In the following run, on a system providing the NPTL threading implementation, the stack size defaults to the value given by the "stack size" resource limit:
$ ulimit -s 8192 # The stack size limit is 8 MB (0x80000 bytes) $ ./a.out hola salut servus Thread 1: top of stack near 0xb7dd03b8; argv_string=hola Thread 2: top of stack near 0xb75cf3b8; argv_string=salut Thread 3: top of stack near 0xb6dce3b8; argv_string=servus Joined with thread 1; returned value was HOLA Joined with thread 2; returned value was SALUT Joined with thread 3; returned value was SERVUSIn the next run, the program explicitly sets a stack size of 1MB (using pthread_attr_setstacksize(3)) for the created threads:
$ ./a.out -s 0x100000 hola salut servus Thread 1: top of stack near 0xb7d723b8; argv_string=hola Thread 2: top of stack near 0xb7c713b8; argv_string=salut Thread 3: top of stack near 0xb7b703b8; argv_string=servus Joined with thread 1; returned value was HOLA Joined with thread 2; returned value was SALUT Joined with thread 3; returned value was SERVUSProgram source
#include <pthread.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <ctype.h> #define handle_error_en(en, msg) \ do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0) #define handle_error(msg) \ do { perror(msg); exit(EXIT_FAILURE); } while (0) struct thread_info { /* Used as argument to thread_start() */ pthread_t thread_id; /* ID returned by pthread_create() */ int thread_num; /* Application-defined thread # */ char *argv_string; /* From command-line argument */ }; /* Thread start function: display address near top of our stack, and return upper-cased copy of argv_string */ static void * thread_start(void *arg) { struct thread_info *tinfo = (struct thread_info *) arg; char *uargv, *p; printf("Thread %d: top of stack near %p; argv_string=%s\n", tinfo->thread_num, &p, tinfo->argv_string); uargv = strdup(tinfo->argv_string); if (uargv == NULL) handle_error("strdup"); for (p = uargv; *p != '\0'; p++) *p = toupper(*p); return uargv; } int main(int argc, char *argv[]) { int s, tnum, opt, num_threads; struct thread_info *tinfo; pthread_attr_t attr; int stack_size; void *res; /* The "-s" option specifies a stack size for our threads */ stack_size = -1; while ((opt = getopt(argc, argv, "s:")) != -1) { switch (opt) { case 's': stack_size = strtoul(optarg, NULL, 0); break; default: fprintf(stderr, "Usage: %s [-s stack-size] arg...\n", argv[0]); exit(EXIT_FAILURE); } } num_threads = argc - optind; /* Initialize thread creation attributes */ s = pthread_attr_init(&attr); if (s != 0) handle_error_en(s, "pthread_attr_init"); if (stack_size > 0) { s = pthread_attr_setstacksize(&attr, stack_size); if (s != 0) handle_error_en(s, "pthread_attr_setstacksize"); } /* Allocate memory for pthread_create() arguments */ tinfo = calloc(num_threads, sizeof(struct thread_info)); if (tinfo == NULL) handle_error("calloc"); /* Create one thread for each command-line argument */ for (tnum = 0; tnum < num_threads; tnum++) { tinfo[tnum].thread_num = tnum + 1; tinfo[tnum].argv_string = argv[optind + tnum]; /* The pthread_create() call stores the thread ID into corresponding element of tinfo[] */ s = pthread_create(&tinfo[tnum].thread_id, &attr, &thread_start, &tinfo[tnum]); if (s != 0) handle_error_en(s, "pthread_create"); } /* Destroy the thread attributes object, since it is no longer needed */ s = pthread_attr_destroy(&attr); if (s != 0) handle_error_en(s, "pthread_attr_destroy"); /* Now join with each thread, and display its returned value */ for (tnum = 0; tnum < num_threads; tnum++) { s = pthread_join(tinfo[tnum].thread_id, &res); if (s != 0) handle_error_en(s, "pthread_join"); printf("Joined with thread %d; returned value was %s\n", tinfo[tnum].thread_num, (char *) res); free(res); /* Free memory allocated by thread */ } free(tinfo); exit(EXIT_SUCCESS); }
关于以上demo部分代码有几个地方需要说明:
线程等待接口pthread_join(),可以等待指定的线程,并且获取等待进程的返回消息(包括pthread_exit()返回码、return 返回、线程被取消时的PTHREAD_CANCEL)。pthread_join()是阻塞等待的,在等待的线程返回前此函数处于阻塞状态,另外线程如果处于脱离状态函数会调用失败,返回EINVAL。
线程的属性及接口,下面章节讲会描述。
pthread_join函数原型:
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
线程取消
线程可以通过调用pthread_cancel()来取消同一进程下的其他线程。线程取消仅仅是向被取消线程提出了个请求,被取消线程实际上会继续执行,根据自身取消方式来选择线程取消时机,有可能是立即取消,也有可能达到下个取消点才真正取消线程。线程取消会返回PTHREAD_CANCLE状态。
#include <pthread.h>
int pthread_cancel(pthread_t thread);
RETURN VALUE
On success, pthread_cancel() returns 0; on error, it returns a non-zero error number.
浙公网安备 33010602011771号