线程-线程控制

知道了线程与进程关系,下来来学习线程控制
下来将要学习的线程库函数是由POSIX标准定义的,称为POSIX thread或者pthread。在Linux
上线程函数位于libpthread共享库中,因此在编译时要加上-lpthread选项。

  • 线程标识:prhread_self
 #include <pthread.h>
 pthread_t pthread_self(void);
返回值:调用线程的线程ID

需要注意的是pthread_self所获取到的是相对于进程的线程控制块的首地址,只是用来描述统一进程当中的不同的线程。而真正的内核当中的线程ID,对于多线程进程来说,每个tid实际是不一样的。
另外,在linux当中如果你要查询系统中的线程,也有一条命令:ps -aL

  • 线程创建:pthread_create
    新增线程可以用pthread_create函数创建
#include <pthread.h>
       int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                          void *(*start_routine) (void *), void *arg);
       返回值:若成功,返回0;否则,返回错误编码

当pthread_create成功返回时,新创建线程的线程ID会被设置成thread指向的内存单元。
attr用于定制各种不同的线程属性,将attr设置成NULL,创建一个具有默认属性的线程。
start_routine新创建的线程从start_routine函数的地址开始运行。
arg为无类型指针参数,是start_routine函数的参数。

/*************************************************************************
    > File Name: my_pthread.c
    > Author: 
    > Mail: 
    > Created Time: Thu 15 Jun 2017 12:51:22 AM PDT
 ************************************************************************/
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
pthread_t tid;
void *thread_run(void *val)
{
    printf("%s : pid is : %d,tid is : %u\n",(char*)val,(int)getpid(),(unsigned long long)pthread_self());
    return NULL;
}
int main()
{
    int err=pthread_create(&tid,NULL,thread_run,"other pthread run");
    if(err!=0)
    {
        printf("pthread_create is faild! info is:%s\n",strerror(err));
        exit(err);
    }
    printf("main thread run : pid is : %d,tid is : %u\n",(int)getpid(),(unsigned long long)pthread_self());
    sleep(1);
    return 0;
}

这里写图片描述
如果任意⼀个线程调⽤了exit或_exit,则整个进程的所有线程都终⽌,由于从main函数return也相当于调⽤exit,为了防⽌新创建的线程还没有得到执⾏就终⽌,我们在main函数return之前延时1秒,这只是⼀种权宜之计,即使主线程等待1秒,内核也不⼀定会调度新创建的线程执⾏。

  • 线程终止:pthread_exit
    进程中任意线程调用exit或_exit,那么整个进程就会终止。那么怎么退出单个线程呢?
    单个线程有三种退出方式:
    (1)可以从线程函数return。当然这不能是主线程,因为主线程return相当于是调用exit。
    (2)线程可以被同一进程的其他线程调用pthread_cancel终止。
    (3)线程调用线程终止函数ptherad_exit。
    相关函数:
    ptherad_exit
#include<pthread.h>
int pthread_exit(void *retval)

retval参数是一个无类型指针,和线程函数返回值的用法一样,其他线程可以调用pthread_join获得这个指针。
需要注意,pthread_exit或者return返回的指针所指向的内存单元必须是全局的或者是⽤
malloc分 配的,不能在线程函数的栈上分配,因为当其它线程得到这个返回指针时线程函数已
经退出了。

  • 线程等待:pthread_join
#include<pthread.h>
int pthread_join(pthread_t thread,void **retval);
返回值:若成功,返回0;否则,返回错误编码

调用该函数的线程将一直阻塞,知道id为thread线程调用pthread_exit终止。
thread线程以不同的⽅法终⽌,通
过pthread_join得到的终⽌状态是不同的,总结如下:
- 如果thread线程通过return返回,value_ptr所指向的单元⾥存放的是thread线程函数的返
回值。
- 如果thread线程被别的线程调⽤pthread_cancel异常终掉,value_ptr所指向的单元⾥存放
的是常数PTHREAD_CANCELED。
- 如果thread线程是⾃⼰调⽤pthread_exit终⽌的,value_ptr所指向的单元存放的是传给
pthread_exit的参数。 如果对thread线程的终⽌状态不感兴趣,可以传NULL给value_ptr参数。
可以通过调用pthread_join自动把线程置于分离状态,这样资源就可以恢复。

#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
void *thread1(void *val)
{
    printf("thread 1 returning...\n");
    return (void*)1;
}
void *thread2(void *val)
{
    printf("thread 2 exiting...\n");
    pthread_exit((void*)2);
}
void *thread3(void *val)
{
    while(1)
    {
        printf("thread 3 is running,wait for be cancal...\n");
        sleep(1);
    }
    return NULL;
}
int main()
{
    pthread_t tid1,tid2,tid3;
    void *tret;
    // pthread 1 return
    pthread_create(&tid1,NULL,thread1,NULL);
    pthread_join(tid1,&tret);
    printf("pthread return.thread id is : %u.return coid is : %d\n",(unsigned long long)tid1,tret);
    // pthread 2 exiting
    pthread_create(&tid2,NULL,thread2,NULL);
    pthread_join(tid2,&tret);  
    printf("pthread exit.thread id is : %u.exit coid is : %d\n",(unsigned long long)tid2,tret);
    //thread 3 cancel by other
    pthread_create(&tid3,NULL,thread3,NULL);
    sleep(3);
    pthread_cancel(tid3);
    pthread_join(tid3,&tret);
    printf("pthread return.thread id is : %u.cancel coid is : %d\n",(unsigned long long)tid3,tret);
    return 0;
}

⼀般情况下,线程终⽌后,其终⽌状态⼀直保留到其它线程调⽤pthread_join获取它的状态为
⽌。 但是线程也可以被置为detach 状态,这样的线程⼀旦终⽌就⽴刻回收它占⽤的所有资源,
⽽不保留终⽌状态。不能对⼀个已经处于detach状态的线程调⽤pthread_join,这样的调⽤将
返回EINVAL。 对⼀个尚未detach的线程调⽤pthread_join或pthread_detach都可以把该线程
置为detach状态,也 就是说,不能对同⼀线程调⽤两次pthread_join,或者如果已经对⼀个线程调
⽤ 了pthread_detach就不能再调⽤pthread_join了。

int pthread_detach(pthread_t thread);
  • 分离线程
    在任何⼀个时间点上, 线程是可结合的(joinable)或者是分离的(detached) 。 ⼀个可
    结合的线程能够被其他线程收回其资源和杀死。在被其他线程回收之前,它的存储器资源
    (例如栈)是不释放的。 相反, ⼀个分离的线程是不能被其他线程回收或杀死的,它的存
    储器资源在它终⽌时由系统⾃动释放。
    一个可结合的线程容易出现类似于僵死进程的问题,一般采用join等待,否则就会出现主线程无法获取到新线程信息,无法回收新线程的资源,这样就会造成内存泄漏的问题。我们在默认的情况下,线程是可结合的。
    如果将线程设为可分离的时,这时候主线程不再等待分离线程,可是分离之后,分离线程由操作系统管理,不再由主线程考虑,在主线程中分离,主线程知道此线程分离了,这样主线程不能join。而如果不在主线程内分离,主线程不知道线程已经分离,有可能主线程依然会进行join。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值