线程控制

一、POSIX线程库
·线程函数库是用户级的,此库为pthread;
·链接线程函数库时要使用编译器命令的“ -lpthread”选项。
二、线程创建

功能:创建一个新的线程
函数原型:
        int pthread_create(pthread_t *thread,const pthread_attr_t *attr, void *(*start_routine)(void *),void *arg);
参数:
    thread:用户级的线程ID;
    attr:设置线程的属性,NULL表示默认属性;
    start_routine:是个函数地址,即线程启动后要执行的函数(回调函数);
    arg:传给线程启动函数的参数。
返回值:成功返回0,失败返回错误码。

错误检查:
**·**pthreads函数出错时不会设置全局变量errno(而大部分POSIX函数会这样做),而是将错误代码通过返回值返回;
**·**pthreads同样也提供了线程内的errno变量,对于pthreads函数的错误码,建议通过返回值来判定,因为读取返回值要比读取线程内的errno变量开销更小。
三、进程ID和线程ID
·在Linux下,线程又被称为轻量级进程,每一个用户态的线程,在内核中都对应一个调度实体,也拥有自己的进程描述符(task_struct结构体);
·没有线程之前,一个进程对应内核里的一个进程描述符,对应一个进程ID;但在引入线程后,一个用户进程下管辖N个用户态线程,每个线程作为一个独立的调度实体在内核态都有自己的进程描述符,则进程和内核的描述符变成了1:N的关系,为了解决进程内的所有线程调用getpid函数时要返回相同的进程ID,则引入了线程组的概念;

进程描述符结构体:
struct task_struct
{
    ...
    pid_t pid;
    pid_t tgid;
    ...
    struct task_struct *group_leader;
    ...
    struct list_head thread_group;
    ...
}

·多线程的进程,又被称为线程组,线程组内的每个线程在内核中都存在一个进程描述符(task_struct)与之对应。上述进程描述符结构体中的pid,对应的是线程ID;tgid对应的是用户层面的进程ID。

如何查看一个线程ID:
ps -eLf | head -1 && ps -eLf | grep a.out | grep -v grep
[root@bogon xiancheng]# ps -eLf | head -1 && ps -eLf | grep a.out | grep -v grep
UID        PID  PPID   LWP  C NLWP STIME TTY          TIME CMD
root      3288  2506  3288  0    2 20:29 pts/0    00:00:00 ./a.out
root      3288  2506  3289  0    2 20:29 pts/0    00:00:00 ./a.out

·PID:指进程ID
·LWP:指线程ID 
·NLWP:线程组内线程的个数
·线程组内的第一个线程,在用户态被称为主线程,在内核中被称为group leader,内核在创建第一个线程时,会将线程组的ID设置为第一个线程的ID,故线程组内存在一个线程ID等于进程ID。
·同一个线程组的线程,没有层次关系。

四、用户级进程ID及进程地址空间布局
**·**pthread_create函数产生并标记在第一个参数指向的地址中的线程ID中,此线程ID属于NPTL线程库;
·线程库NPTL提供了pthread_self函数,可以获得线程自身的ID。

pthread_t pthread_self(void);
对NPTL库而言,pthread_t类型的线程ID,本质就是一个进程地址空间上的一个地址。(即,共享库当中对应描述自己结构的首地址)

这里写图片描述
五、线程终止
如果要终止某个线程而不终止整个进程,有三种方法:
(1)从线程函数return。对主线程不适用,从main函数return相当于调用exit;
(2)线程可以调用pthread_exit终止自己;
(3)一个线程可以调用pthread_cancel终止同一进程中的另一个线程。

pthread_exit函数:
功能:线程终止
函数原型:
        void pthread_exit(void *value_ptr);
参数:
        value_ptr:value_ptr不要指向一个局部变量
返回值:无返回值。
注意:pthread_exitreturn返回的指针所指向的内存单元必须是全局的或者使用malloc分配的,不能在线程函数的栈上分配,因为当其他线程得到这个返回指针时线程函数已经退出了。
pthread_cancel函数:
功能:取消一个执行中的线程
函数原型:
        int pthread_cancel(pthread_t thread);
参数:
        thread;线程ID
返回值:成功返回0,失败返回错误码。

线程是可以被取消的,线程被取消,返回值为-1.

六、线程等待与分离
线程一旦被创建就必须被等待,否则会造成类似于僵尸进程的内存泄漏问题。
·线程等待原因:
·已经退出的线程,其空间没有被释放,仍然在进程的地址空间内;
·创建新的进程不会复用刚才退出线程的地址空间。

功能:等待线程结束
原型: 
    int pthread_join(pthread_t thread,void **value_ptr);
参数:
    thread;线程ID
    value_ptr:指向一个指针,后者指向线程的返回值
返回值:成功返回0,失败返回错误码。

调用pthread_join函数的线程将挂起等待,直到id为thread的线程终止。
thread线程以不同的方法终止,通过pthread_join得到的终止状态是不同的:
(1)若thread线程通过return返回,则value_ptr所指向的单元里存放的是thread线程函数的返回值;
(2)若thread线程被别的线程调用pthread_cancel异常终止,则value_ptr所指向的单元里存放的是-1;
(3)若thread是自己调用pthread_exit终止的,则value_ptr所指向的单元存放的是传给pthread_exit的参数;
(4)若对thread线程的终止状态不关心,则可传NULL给value_ptr参数。
·分离线程
·默认情况下,新创建的线程是joinable的,线程退出后,需要对其进行pthread_join操作,否则无法释放资源,从而造成系统内存泄漏;
·若不关心线程的返回值,join是一种负担,此时,我们可以告诉系统,当线程退出时,自动释放线程资源。

线程一旦被设为分离,主线程不需要再等待该线程,该线程退出时系统会自动回收其资源。
int pthread_detach(pthread_t thread);

可以是线程组内其他线程对目标线程进行分离,也可以是线程自己分离;
pthread_detach(pthread_self());

joinable和分离是冲突的,一个线程不能既是joinable又是分离的。(即,一个线程必须被等待,若不想join,就必须被分离)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值