请看 pthread primier 一书
什么是一个thread?
线程是1个程序的一个控制流。
IEEE POSIX指定了统一的线程标准
pthread。它是glibc库的组成部分。
在Linux 2.4内核中, Linux内核中使用了一个内核线程来处理用户态进程中的多个线程的上下文切换(线程切换)。由于当时硬件并没有
线程寄存器之类的冬冬来支持多线程,因此线程的切换性能很低下,并且需要引入复杂的机制在进程的栈中为各个线程划分出各自的栈数据所在位置,并且在切换时进行栈数据拷贝。
最大的问题是
内核中缺乏对线程间的同步机制的支持,因此pthread库不得不在底层依靠信号方式来实现同步,因此线程互斥中的互斥量操作和条件量操作都转换为进程的信号操作。而
信号操作比较慢且不太可靠。
IA-32硬件结构中,出现了对线程寄存器的支持,因此Pthread的线程上下文切换速度有了很大提高。但是由于硬件限制局限,线程的数量必须小于
8192个。
NPTL (Native POSIX Thread Library)NPTL的主要特征:
首先在IA-32和x86-64位体系结构上能实现任意数量的线程数量。通过引入了TLS系统调用可以建立多个GDT全局描述符表,每个cpu维护一个描述符表,每个表项存放一个线程。
其次,clone系统调用优化了线程的建立和结束功能。也不再需要额外的调度线程的帮助就可以回收线程资源了。
其三,信号投递由内核完成,而不再需要额外的用户态管理线程的帮助,而严重错误信号之间结束整个进程。
其四,引入了新的退出系统调用exit_group()。原来的exit保留用于退出单个线程,exit_group用于退出整个进程。
其五, 新的exec调用会先结束到一个进程中的所有线程后再载入新程序的执行,而不是只结束调用的线程。
其六,所有线程资源使用情况(cpu资源,内存资源)会报告给整个进程,而不再是只报告给初始化线程。
其七,proc文件系统中只显示初始化线程的信息,而不再是所有线程的信息(上万个线程会把proc文件系统拖死)。
其八, 支持线程脱离, 执行Pthread_join的线程不需要再执行no wait。
其九,由内核来维护初始化线程(变成内核线程了),并在proc文件系统中显示其状态,并维护直到所有线程退出来保证信号的投递。
其十,内核支持无限制的线程数量。
通过ps,可以看出新旧Linux线程库的区别。
要使用NPTL,Linux必须是
2.6的核。
确定你的gcc支持NPTL的方法是用#getconf GNU_LIBPTHREAD_VERSION命令来查看gcc的编译时的对多线程的支持方式。比如在gcc 4.0.2上显示结果NPTL 2.3.5。如果返回的是linuxthreads-0.10,说明你的gcc不支持NPTL。
至于如何在开发中使用NPTL可参考《Migrating to Linux kernel 2.6 -- Part 5: Migrating
apps to the 2.6 kernel and
NPTL》(
http://linuxdevices.com/articles/AT6753699732.html)
thread的优势与缺点:
创建线程的开销比创建进程小很多。而且有时,需要同时作2个或以上的事情。
缺点是--难以设计;难以调试。
#include <
pthread.h>
创建线程(父线程)
int
pthread_create(pthread_t *thread, pthread_attr_t *attr, void *(*start_routine)(void *), void *arg);
其中,线程函数接口: void *(*start_routine)(void *)
线程属性attr在后面有讲。
arg是子线程输入参数。
线程结束(子线程)
void
pthread_exit(void *retval);
retval是子线程输出参数。
线程合并(父线程)
int
pthread_join(pthread_t pt, void **thread_return);
#include
#include
#include
#include
void *thread_function(void *arg);
char message[] = "Hello World";
int main() {
int res;
pthread_t a_thread;
void *thread_result;
res = pthread_create(&a_thread, NULL, thread_function, (void *)message);
if (res != 0) {
perror("Thread creation failed");
exit(EXIT_FAILURE);
}
printf("Waiting for thread to finish...\n");
res = pthread_join(a_thread, &thread_result);
if (res != 0) {
perror("Thread join failed");
exit(EXIT_FAILURE);
}
printf("Thread joined, it returned %s\n", (char *)thread_result);
printf("Message is now %s\n", message);
exit(EXIT_SUCCESS);
}
void *thread_function(void *arg) {
printf("thread_function is running. Argument was %s\n", (char *)arg);
sleep(3);
strcpy(message, "Bye!");
pthread_exit("Thank you for the CPU time");
}
线程同步
有2种方法:信号量和互斥锁。
1)信号量
#include
int sem_init(sem_t *sem, int pshared, unsigned int value);
初始化1个信号sem,对Linux来讲,pshared应固定为0(表示线号限制在进程内),value是初始值。
int sem_wait(sem_t * sem);
信号量自动减1.如果信号量为0,将停顿直到变为非零才执行-1操作并继续运行下去。
int sem_post(sem_t * sem);
信号量自动加1.
int sem_destroy(sem_t * sem);
销毁信号sem。
#include
#include
#include
#include
#include
#include
void *thread_function(void *arg);
sem_t bin_sem;
#define WORK_SIZE 1024
char work_area[WORK_SIZE];
int main() {
int res;
pthread_t a_thread;
void *thread_result;
res = sem_init(&bin_sem, 0, 0);
if (res != 0) {
perror("Semaphore initialization failed");
exit(EXIT_FAILURE);
}
res = pthread_create(&a_thread, NULL, thread_function, NULL);
if (res != 0) {
perror("Thread creation failed");
exit(EXIT_FAILURE);
}
printf("Input some text. Enter 'end' to finish\n");
while(strncmp("end", work_area, 3) != 0) {
fgets(work_area, WORK_SIZE, stdin);
sem_post(&bin_sem);
}
printf("\nWaiting for thread to finish...\n");
res = pthread_join(a_thread, &thread_result);
if (res != 0) {
perror("Thread join failed");
exit(EXIT_FAILURE);
}
printf("Thread joined\n");
sem_destroy(&bin_sem);
exit(EXIT_SUCCESS);
}
void *thread_function(void *arg) {
sem_wait(&bin_sem);
while(strncmp("end", work_area, 3) != 0) {
printf("You input %d characters\n", strlen(work_area) -1);
sem_wait(&bin_sem);
}
pthread_exit(NULL);
}
2)互斥锁
互斥锁是另外1种方式。
通过锁来确保只有唯一对象执行访问操作。当进入临界区之前,锁住一个对象,离开临界区之后解开锁。
#include
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);
锁的属性muteattr可以控制互斥锁的行为特性。如果不关心的话,可以设置为NULL。它的细节超出了本书范围。
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_lock(pthread_mutex_t *mutex));
int pthread_mutex_unlock(pthread_mutex_t *mutex);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
#include
#include
#include
#include
#include
#include
void *thread_function(void *arg);
pthread_mutex_t work_mutex; /* protects both work_area and time_to_exit */
#define WORK_SIZE 1024
char work_area[WORK_SIZE];
int time_to_exit = 0;
int main() {
int res;
pthread_t a_thread;
void *thread_result;
res = pthread_mutex_init(&work_mutex, NULL);
if (res != 0) {
perror("Mutex initialization failed");
exit(EXIT_FAILURE);
}
res = pthread_create(&a_thread, NULL, thread_function, NULL);
if (res != 0) {
perror("Thread creation failed");
exit(EXIT_FAILURE);
}
pthread_mutex_lock(&work_mutex);
printf("Input some text. Enter 'end' to finish\n");
while(!time_to_exit) {
fgets(work_area, WORK_SIZE, stdin);
pthread_mutex_unlock(&work_mutex);
while(1) {
pthread_mutex_lock(&work_mutex);
if (work_area[0] != '\0') {
pthread_mutex_unlock(&work_mutex);
sleep(1);
}
else {
break;
}
}
}
pthread_mutex_unlock(&work_mutex);
printf("\nWaiting for thread to finish...\n");
res = pthread_join(a_thread, &thread_result);
if (res != 0) {
perror("Thread join failed");
exit(EXIT_FAILURE);
}
printf("Thread joined\n");
pthread_mutex_destroy(&work_mutex);
exit(EXIT_SUCCESS);
}
void *thread_function(void *arg) {
sleep(1);
pthread_mutex_lock(&work_mutex);
while(strncmp("end", work_area, 3) != 0) {
printf("You input %d characters\n", strlen(work_area) -1);
work_area[0] = '\0';
pthread_mutex_unlock(&work_mutex);
sleep(1);
pthread_mutex_lock(&work_mutex);
while (work_area[0] == '\0' ) {
pthread_mutex_unlock(&work_mutex);
sleep(1);
pthread_mutex_lock(&work_mutex);
}
}
time_to_exit = 1;
work_area[0] = '\0';
pthread_mutex_unlock(&work_mutex);
pthread_exit(0);
}
线程属性
有一些线程的属性,是可以被控制的。如果需要线程返回数据给创建它的主线程,需要pthread_join. 但如果不需要,我们可以调用pthread_detach来创建分离的线程detached threads。it不需要rejoin主线程。
#include
int pthread_attr_init(pthread_attr_t *attr);
这是我们需要的最重要的属性功能,它初始化一个线程属性对象。
与之对应的是 pthread_attr_destroy,作用是允许干净地清除属性对象。一旦属性对象被销毁,就只能再次被初始化之后才能被再次使用。
一旦一个属性对象被创建后,就可以通过很多函数来设置线程的各种属性。
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate);
int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy);
int pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy);
int pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param
*param);
int pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param
*param);
int pthread_attr_setinheritsched(pthread_attr_t *attr, int inherit);
int pthread_attr_getinheritsched(const pthread_attr_t *attr, int *inherit);
int pthread_attr_setscope(pthread_attr_t *attr, int scope);
int pthread_attr_getscope(const pthread_attr_t *attr, int *scope);
int pthread_attr_setstacksize(pthread_attr_t *attr, int scope);
int pthread_attr_getstacksize(const pthread_attr_t *attr, int *scope);
detachedstate: PTHREAD_CREATE_JOINABLE,PTHREAD_CREATE_DETACHED。前者是缺省值。
policy: SCHED_OTHER, SCHED_RP, SCHED_FIFO. 后两者只用于处理高级权限的情况。
sched_param:对以SCHED_OTHER运行的线程的调度进行进一步的控制。
inherit:PTHREAD_EXPLICIT_SCHED,PTHREAD_INHERIT_SCHED。是直接定义属性,还是从创建着线程继承属性。
scope: Linux只支持一个值 PTHREAD_SCOPE_SYSTEM
stacksize: 只在定义了_POSIX_THREAD_ATTR_STACKSIZE时才被支持。这是个任选项,linux使用了很多的堆栈来实现线程。所以,这个项是多余的。
线程清除
删除1个线程,可以发信号。除了这种方式外,还有1种方式:
int pthread_cancel(pthread_t thread);
发送端比较简单,指明被删的线程号即可
int pthread_setcancelstate(int state, int *oldstate);
接收端稍微麻烦点,
state: 是个开关,PTHREAD_CANCEL_ENABLE or PTHREAD_CANCEL_DISABLE。PTHREAD_CANCEL_ENABLE 是缺省的。
oldstate:不干兴趣的话,就填写NULL好了。
接收后,就进入第2级控制
int pthread_setcanceltype(int type, int *oldtype);
type:
PTHREAD_CANCEL_ASYNCHRONOUS(立刻删除),PTHREAD_CANCEL_DEFERRED(等到
pthread_join, pthread_cond_wait,
pthread_cond_timewait,pthread_testcancel, sem_wait, 或者sigwait被执行后才删除线程)。PTHREAD_CANCEL_DEFERRED是缺省的.
oldtype:不感兴趣的话,可以添NULL。
#include
#include
#include
#include
void *thread_function(void *arg);
int main() {
int res;
pthread_t a_thread;
void *thread_result;
res = pthread_create(&a_thread, NULL, thread_function, NULL);
if (res != 0) {
perror("Thread creation failed");
exit(EXIT_FAILURE);
}
sleep(3);
printf("Canceling thread...\n");
res = pthread_cancel(a_thread);
if (res != 0) {
perror("Thread cancelation failed");
exit(EXIT_FAILURE);
}
printf("Waiting for thread to finish...\n");
res = pthread_join(a_thread, &thread_result);
if (res != 0) {
perror("Thread join failed");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
void *thread_function(void *arg) {
int i, res;
res = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
if (res != 0) {
perror("Thread pthread_setcancelstate failed");
exit(EXIT_FAILURE);
}
res = pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
if (res != 0) {
perror("Thread pthread_setcanceltype failed");
exit(EXIT_FAILURE);
}
printf("thread_function is running\n");
for(i = 0; i < 10; i++) {
printf("Thread is still running (%d)...\n", i);
sleep(1);
}
pthread_exit(0);
}
跑很多线程
#include
#include
#include
#include
#define NUM_THREADS 6
void *thread_function(void *arg);
int main() {
int res;
pthread_t a_thread[NUM_THREADS];
void *thread_result;
int lots_of_threads;
for(lots_of_threads = 0; lots_of_threads < NUM_THREADS; lots_of_threads++) {
res = pthread_create(&(a_thread[lots_of_threads]), NULL, thread_function,
(void *)&lots_of_threads);
if (res != 0) {
perror("Thread creation failed");
exit(EXIT_FAILURE);
}
sleep(1);
}
printf("Waiting for threads to finish...\n");
for(lots_of_threads = NUM_THREADS - 1; lots_of_threads >= 0; lots_of_threads—)
{
res = pthread_join(a_thread[lots_of_threads], &thread_result);
if (res == 0) {
printf("Picked up a thread\n");
}
else {
perror("pthread_join failed");
}
}
printf("All done\n");
exit(EXIT_SUCCESS);
}
void *thread_function(void *arg) {
int my_number = *(int *)arg;
int rand_num;
printf("thread_function is running. Argument was %d\n", my_number);
rand_num=1+(int)(9.0*rand()/(RAND_MAX+1.0));
sleep(rand_num);
printf("Bye from %d\n", my_number);
pthread_exit(NULL);
}
阅读(1139) | 评论(0) | 转发(0) |