线程是操作系统能够进行独立调度和分派的基本单位。在类UNIX系统中,通常情况下一个进程可以被看做只有一个控制线程。一个线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每个线程并行执行不同的任务。
同一进程中的多个线程将共享该进程中的全部系统资源,包括可执行的程序文本、虚拟地址空间、全局内存、堆内存、文件描述符和信号处理等。但同一进程中的多个线程有各自的调用栈,自己的寄存器上下文,线程ID以及线程私有数据。
这里对POSIX线程操作做一些简单描述记录,并结合一个例子,深化理解。最后对与线程相关的一些函数做个大概的介绍。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <pthread.h>
#include <errno.h>
#define handle_error_en(err, msg) \
do { errno = err; perror(msg); exit(EXIT_FAILURE); } while (0)
static int done = 0;
static int cleanup_pop_arg = 0;
static int cnt = 0;
static void * cleanup_handler()
{
printf("called cleanup handler\n");
cnt = 0;
return NULL;
}
void * thread_start(void *arg)
{
time_t start, curr;
printf("new thread started\n");
printf("thread id is: %lu\n", pthread_self()); //获取线程自己的ID
pthread_cleanup_push(cleanup_handler, NULL); //设置线程退出时将调用的清理函数
curr = start = time(NULL);
while (!done) {
pthread_testcancel(); //设置响应其他线程的取消申请时,具体的退出点
if (curr < time(NULL)) {
curr = time(NULL);
printf("cnt = %d\n", cnt);
cnt++;
}
}
pthread_cleanup_pop(cleanup_pop_arg); //调用cleanup_pop_arg非零值时,会调用 清理函数cleanup_handler
// if (done)
// pthread_exit((void *)1); //调用pthread_exit()时,线程会调用 清理函数
return NULL;
}
int main(int argc, char *argv[])
{
pthread_t thr;
int err;
void *res;
err = pthread_create(&thr, NULL, thread_start, NULL); //创建新线程
if (err != 0)
handle_error_en(err, "pthread_create");
sleep(2);
if (argc > 1) {
if (argc > 2)
cleanup_pop_arg = atoi(argv[2]);
done = 1;
}
else {
printf("canceling thread\n");
err = pthread_cancel(thr); //对新建的线程发出取消请求
if (err != 0)
handle_error_en(err, "pthread_cancel");
}
err = pthread_join(thr, &res); //获取线程的退出状态,主线程会阻塞
if (err != 0)
handle_error_en(err, "pthread_join");
if (res == PTHREAD_CANCELED)
printf("thread was canceled; cnt = %d\n", cnt);
else
printf("thread terminated normally; cnt = %d\n", cnt);
exit(EXIT_SUCCESS);
}
------------------------------------------------------------------------
对以上这个小例子做测试:
[[email protected] ~]$ ./cleanup2 // argc == 1
new thread started
thread id is: 140498886080256
cnt = 0
cnt = 1
canceling thread
called cleanup handler
thread was canceled; cnt = 0
[[email protected] ~]$ ./cleanup2 1 // argc == 2
new thread started
thread id is: 139745109219072
cnt = 0
cnt = 1
thread terminated normally; cnt = 2
[[email protected] ~]$ ./cleanup2 1 1 // argc == 3 argv[2] = 1 非零值
new thread started
thread id is: 140498886080256
cnt = 0
cnt = 1
canceling thread
called cleanup handler
thread was canceled; cnt = 0
[us[email protected] ~]$ ./cleanup2 1 0 // argc == 3 argv[2] = 0 零值
new thread started
thread id is: 139975075624704
cnt = 0
cnt = 1
thread terminated normally; cnt = 2
--------------------------------------------------------------------------
线程创建
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg)
参数:
pthread_t *thread_id : 指向的内存区域用于保存新创建的线程ID
const pthread_attr_t *attr :用于设置线程的属性,默认设置为NULL
void *(*start_routine) (void *) :这是一个函数指针,新创建的线程将从这个函数地址开始执行
void *arg :无类型指针参数,传递给start_routine
返回值:
线程被成功创建 返回0,失败放回错误码
获取线程ID
pthread_t pthread_self(void)
返回值:
线程自己的ID
获取线程退出状态
int pthread_join(pthread_t thread, void **retval)
参数:
pthread_t thread_id : 要获取退出状态的线程ID
void **retval : 线程的退出码。创建的线程正常返回,这里会保存线程的返回码,如果线程时被取消的(调用pthread_cancel),retval指向的内存单元被置为PTHREAD_CANCELED
返回值:
线程被成功创建 返回0,失败放回错误码
线程退出
void pthread_exit(void *retval)
参数:
void *retval :如果线程是可连接的,会通过retval返回一个值,这个值可以用pthread_join获取
申请取消其他线程
int pthread_cancel(pthread_t thread)
参数:
pthread_t thread_id : 需要申请取消的线程ID
返回值:
成功:0, 失败:错误码
设置和删除线程退出时需要执行的函数,这两个函数通常都是成对出现
void pthread_cleanup_push(void (*routine)(void *), void *arg)
参数:
void (*routine)(void *) :注册线程退出时需要调用的线程清理程序
void *arg : 传递给线程清理程序的参数
void pthread_cleanup_pop(int execute)
参数:
int execute :非零值会调用线程清理程序,设置为0,线程清理程序不会被调用。
备注:线程调用pthread_exit()和响应其他线程的申请取消请求的情况下,任然会调用线程清理程序,但不管是哪种方式都会删除掉有pthread_cleanup_push设置的线程清理程序。