天天看点

pthread线程编程基础

线程是操作系统能够进行独立调度和分派的基本单位。在类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设置的线程清理程序。

继续阅读