《POSIX多线程程序设计》——David R.Buten
主线程是特殊的
当c语言运行时,首先执行main函数,这个特殊的执行流被称为“主线程”。主线程的特殊性在于Pthreads在main函数返回阶段保留了传统UNIX进程行为。即当进程结束时,所有线程、状态和它们的工作结果都会简单的“蒸发”——没有理由要清理什么。
在大多数系统中,主线程运行在默认进程堆栈上,该堆栈可以增长到足够的尺寸;而在某些系统中,普通线程的堆栈空间是受限的,如果线程堆栈溢出,则程序会因段错误或总线错误而失败。
(例子:在main函数中调用pthread_exit,这样进程就必须等到所有线程结束后才能终止)
#include <errno.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#define handle_error_en(en, msg) \
do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0)
static void* child_thread(void *arg) {
while (1) {
printf("%lu\n", time(NULL));
sleep(1);
}
}
int main(void) {
int status;
pthread_t thread;
status = pthread_create(&thread, NULL, child_thread, NULL);
if (0 != status)
handle_error_en(status, "pthread_create");
pthread_exit(NULL); // 主线程终止,但进程会等待所有线程结束后才终止
return 0;
}
分离一个正在运行的线程不会对线程带来任何影响
分离线程仅仅是通知系统当该线程结束时,其所属资源可以被回收
- 如果要创建一个从不需要控制的线程,可以使用属性(attribute)来建立线程以使它总是可分离的。
- 如果不想等待创建的某个线程,而且知道不再需要控制它,可以随时调用pthread_detach函数来分离它(线程可以分离自己)。
- 如果需要获得线程的返回值,或者需要获知其何时结束,应该调用pthread_join函数,pthread_join函数将阻塞其调用者直到指定线程终止(调用pthread_join函数自动分离指定的线程)。
- 如果多个线程需要知道某个特定线程何时结束,则这些线程应该等待某个条件变量而不是调用pthread_join函数。
如果pthread_join恰恰实现了你想要的功能,则一定要使用它。由于pthread_join是最受限的传递返回值的模型,不要扭曲自己的设计来满足pthread_join的限制。
(例子:使用pthread_join函数和pthread_detach函数)
#include <errno.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define err_abort(code, text) do { \
fprintf(stderr, "%s at \"%s\":%d: %s\n", \
text, __FILE__, __LINE__, strerror(errno)); \
abort(); \
} while (0)
void * detach_thread(void *arg) {
int status;
status = pthread_detach(pthread_self()); // 分离自己
if (0 != status) err_abort(status, "Detach thread");
sleep(3); // 3s后终止
printf("detach_thread() exit\n");
return NULL;
}
void* join_thread(void *arg) {
return arg;
}
int main(int argc, char *argv[]) {
pthread_t join_id, detach_id;
void *thread_result;
int arg = 123;
int status;
status = pthread_create(&join_id, NULL, join_thread, &arg);
if (status != 0) err_abort(status, "Create Join thread");
status = pthread_create(&detach_id, NULL, detach_thread, NULL);
if (status != 0) err_abort(status, "Create Detach thread");
status = pthread_join(join_id, &thread_result); // 阻塞等待线程终止
if (status != 0) err_abort(status, "Join thread");
printf("thread_result=%d\n", *(int *)thread_result);
pthread_exit(NULL); // 终止主线程,保留进程
return 0;
}
结果:
thread_result=123
detach_thread() exit
互斥量
- 互斥量是用
pthread_mutex_t
类型表示,不能拷贝互斥量变量,因为使用拷贝的互斥量是不确定的。可以拷贝指向互斥量的指针。 - 必须保证每个互斥量在使用前被初始化,而且只被初始化一次。可以在创建任何线程之前初始化它,如通过调用
pthread_once
。 - Pthreads提供了
pthread_mutex_trylock
函数(非阻塞式互斥量),当调用互斥量已被锁住时调用该函数将立即返回错误代码EBUSY。使用pthread_mutex_trylock
加锁函数时,需确保只有当此函数调用成功时,才能解锁互斥量。只有拥有互斥量的线程才能解锁它。
(trylock.c:使用pthread_mutex_trylock
函数间歇性地报告计数器的值,不过仅当它对计数器的访问与计数线程没有发生冲突时才报告)
#include <errno.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define err_abort(code, text) do { \
fprintf(stderr, "%s at \"%s\":%d: %s\n", \
text, __FILE__, __LINE__, strerror(errno)); \
abort(); \
} while (0)
#define SPIN 1000000
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // 静态初始化互斥锁