APUE学习-线程(1)

来源:互联网 发布:淘宝开过店铺怎么注销 编辑:程序博客网 时间:2024/05/01 02:44

线程(thread)----轻量级的进程、CPU调度的最小单位,相比较于进程,进程是分配资源的最小单位。

之前讲到的是多进程编程,这一部分要说的是如何在一个进程中实现多线程编程(当然将进程部分的内容放到一起,就可以实现多进程多线程编程)。POSIX(可移植性操作系统接口)规定了可移植性的线程库pthread库<pthread.h>,这里面的函数需要在编译时加上-lpthread(-pthread)参数,pthread库中的类型以及函数是不透明的(掌握如何使用,无须关心如何实现)。

一个进程中至少有一个线程,从main()开始运行的线程称为 主线程(初始线程)。线程id类型pthread_t,可以使用pthread_self()返回,需要注意的是,线程id只是在一个进程内有效(进程id在整个系统中有效);使用pthread_equal()比较两个线程的id是否相等。

[cpp] view plaincopy
  1. pthread_t pthread_self(void);  
  2. int pthread_equal(pthread_t tid1, pthread_t tid2);  
线程创建的创建使用pthread_create()函数:

[cpp] view plaincopy
  1. int pthread_create(pthread_t *tidp, const pthread_attr_t *attr, void *(*start_rtn)(void*), void *arg);  
函数的第一个参数tidp,是一个出参,用来带回被创建线程的tid;第二个参数attr设置的线程属性(后面说明),默认属性传NULL即可;第三个参数start_rtn是一个函数指针,是被创建线程的入口函数(新线程从该函数开始运行至该函数结束);第四个参数arg是start_rtn函数的参数;该函数调用成功返回0,否则返回出错编码(不使用errno全局变量)。几点需要注意:

1.受OS调度影响,被创建的线程处于就绪状态,故创建线程的线程往往是先被调用。

2.同一进程中的多个线程共享内存资源,故多个线程对进程中的所有变量都是共享的(但要注意该变量的生存期和作用域)。

3.只要进程(主线程)退出,其余未结束的线程都随之结束了(进程结束资源也就没了)

4.同一进程中的多个线程共享文件描述符,并非复制一份。

5.同一进程中的多个线程共享进程的信号屏蔽字,但新线程的未决信号集讲被清空。

进程退出的方式:从start_rtn返回;自身调用pthread_exit()函数;被同一个进程中的其他线程取消。先来看pthread_exit()函数和与之配套的pthread_join()函数:

[cpp] view plaincopy
  1. void pthread_exit(void *rval_ptr);  
  2. int pthread_join(pthread_t threadvoid **rval_ptr);  
pthread_exit()函数类似于进程中的exit()函数,线程直接终止,并将rval_ptr参数作为线程的返回值。pthread_join()的功能类似进程的waitpid(pid)函数,阻塞等待指定的线程退出并接受返回值,参数是void **类型,是一个出参参数。

线程中也有类似进程atexit()注册清理操作的函数,pthread_cleanup_push() pthread_cleanup_pop()注册线程的清理函数。

[cpp] view plaincopy
  1. void pthread_cleanup_push(void (*rtn)(void *), void *arg);  
  2. void pthread_cleanup_pop(int execute);  
注册的函数有两个参数,一个是函数指针,一个是该函数接收的参数。第二个函数在执行后会设置之前注册的函数的执行状态,如果execute为0则不执行否则执行,需要注意的是,这两个函数必须是在一个函数内成对出现(具体实现应该是带括号的宏定义),但可以考虑放到pthread_exit()后。
再来说下pthread_cancel()函数,它和pthread_exit()都能使一个线程退出,不同是后者是使线程本身退出,而前者是使同一进程中的其他线程退出。

[cpp] view plaincopy
  1. int pthread_cancel(pthread_t tid);  
该函数的原理是:向tid线程发送SIGCANCEL信号(这个信号在kill -l中查询不到)。只是发送一个信号作为请求,但指定的线程是否退出何时退出pthread_cancel()并不关心,这就涉及到一个问题,一个线程在接收到SIGCANCEL信号时何时退出,有个取消点的概念。大部分的pthread函数以及阻塞的系统调用都是取消点,增加设置取消点的函数是pthread_testcancel():

[cpp] view plaincopy
  1. void pthread_testcancel(void);  
通过这个函数可以设置取消点。取消点涉及到可取消状态和取消类型的的概念,相关操作如下:

[cpp] view plaincopy
  1. int pthread_setcancelstate(int state, int *oldstate);  
  2. int pthread_setcanceltype(int type, int *oldtype);  
分别用来设置取消状态和取消类型。取消状态包括PTHREAD_CANCEL_ENBALE可以被取消 PTHREAD_CANCEL_DISABLE不允许被取消;取消类型包括PTHREAD_CANCEL_DEFERRED直到取消点才终止 PTHREAD_CANCEL_ASYNCHRONOUS立即终止(这个在很多平台下是不好用的)

再介绍一下其他的线程属性,线程属性的类型是pthread_attr_t类型,类型的初始化和销毁分别是pthread_attr_init()  pthread_attr_destroy()两个函数:

[cpp] view plaincopy
  1. int pthread_attr_init(pthread_attr_t *attr);  
  2. int pthread_attr_destroy(pthread_attr_t *attr);  
线程的属性包括:分离属性 绑定属性 线程栈属性等等,这里我们只说下分离属性(其余的用不到)。如果一个线程被分离了,那么创建它的线程即使调用了pthread_join()也不能等待并接受该线程的返回值。设置分离属性有两种方法,第一种是直接调用pthread_detach()函数,第二个中是将线程属性传入到pthread_create()函数中。

[cpp] view plaincopy
  1. int pthread_detach(pthread_t tid);  
  2. int pthread_attr_getdetachstate(const pthread_attr_t * attr,  int *detachstate);  
  3. int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);  

pthread_detach()函数使tid线程分离,后面两个函数操作的是线程属性的结构,detachstate的值可以是PTHREAD_CREATE_JOINABLE不分离(默认的) PTHREAD_CREATE_DETACHED分离。这两种方法的区别是,第一种用于线程已被创建后分离,另一种用于创建前设置。


0 0
原创粉丝点击