线程创建,线程等待,线分离,线程终止

线程的创建、等待、分离、终止

本文所实现的所有线程均是POSIX标准的用户级线程。
一.  POSIX线程库
1.与线程有关的函数构成了一个系列,绝大多数的名字都是以“pthread_”打头的
2.要使用这些函数库,必须引入头文件“#include"
3.链接这些线程函数库时要使用编译器命令的“-lpthread”选项

二. 线程控制
1. 创建线程
(1)函数原型:

线程创建,线程等待,线分离,线程终止_第1张图片
(2)函数功能:创建一个新线程
(3)函数参数:

        1)thread:输出型参数,用于返回新创建的线程id
        2)attr:设置线程的属性,为NULL时表示使用默认属性
        3)start_routine:函数指针,指向线程启动后要执行的函数
        4)arg:传给线程启动后要执行函数的参数
(4)函数返回值:成功返回0,失败返回错误码
(5)测试代码:

//线程的创建                                                                                                                                                            
#include 
#include 
#include 

void* thread_run(void* arg)//传入线程创建的函数
{
    while(1)
    {
        printf("I am thread 1\n");
        sleep(2);
    }
}

int main()
{
    pthread_t tid;//无符号长整型
    //新线程去执行thread_run函数
    int ret = pthread_create(&tid, NULL, thread_run, "thread 1");//这里给thread_run传入的参数为一个字符串

    //这之后的是主线程在执行
    while(1)
    {
        printf("I am main thread\n");
        sleep(1);
    }
    return 0;
}

运行结果:
线程创建,线程等待,线分离,线程终止_第2张图片
        可以看到,该程序创建好之后,有两个执行流,主线程执行一个循环,新线程执行一个循环。这样的情况在单线程的程序结果中是不可能出现的。
2.线程等待

        与进程等待类似,线程也需要等待。因为退出的线程空间不会被释放,仍然在进程的地址空间中,且创建其他新的线程不会复用已退出线程的地址空间。
(1)函数原型:

线程创建,线程等待,线分离,线程终止_第3张图片
(2)函数功能:主线程等待新线程结束退出,这里是阻塞式等待
(3)函数参数:

        1)thread:线程id
        2)retval:输出型参数,用于存放线程的退出信息
(4)函数返回值:成功返回0,失败返回错误码
注意,thread函数以不同方式终止,得到的终止状态是不同的:
        若thread线程通过return返回,retval指向的单元里存放的是thread线程函数的返回值;
     若thread线程被别的函数调用pthread_cancel异常终止掉,retval指向的单元里存放的是常数PTHREAD_CANCELED,其中,该常数是(void*)-1;
        thread线程是自己调用pthread_exit终止的,retval指向的单元里存放的是传给pthread_exit的参数;
        若对thread线程的退出信息不在意,可传入NULL。

(5)测试代码:

//线程的等待                                                                                                                                                            
#include 
#include 
#include 

void* thread_run(void* arg)//传入线程创建的函数
{
    //pthread_self函数用于获得当前线程id
    printf("I am %s, id is %lu\n", (char*)arg, pthread_self());
    sleep(2);
    return ((void*)132);
}

int main()
{
    pthread_t tid;//无符号长整型
    //新线程去执行thread_run函数
    int ret = pthread_create(&tid, NULL, thread_run, "thread 1");//这里给thread_run传入的参数为一个字符串
    //这之后的是主线程在执行
    printf("new: thread id id %lu\n", tid);
    printf("main: thread id id %lu\n", pthread_self());
    
    void* ptr;
    //这里阻塞式等待
    pthread_join(tid, &ptr);
    printf("main thread run, new thread ret: %d\n", (int)ptr);
    return 0;
}

运行结果:
        可以看到,主线程等待,看到新线程的退出码为132。
3. 线程分离

        默认状态下,新创建的线程是joinable的,线程退出后,要对其进行等待操作,否则无法释放资源,从而造成系统泄漏。但是,若系统不关心新创建线程的返回值,等待是一种负担,这种时候我们就可以对新创建的线程进行分离操作。分离后的线程,在退出时,会自动释放系统资源。
(1)函数原型:

线程创建,线程等待,线分离,线程终止_第4张图片
(2)函数功能:分离线程,使得在其退出后,自动释放系统资源
(3)函数参数:

        thread:要进行分离操作的线程id
(4)函数返回值:成功返回0,失败返回错误码

注:
        可以是线程组内其他进程对目标进程进行分离,也可以是线程自己分离;
        joinable与分离是冲突的,线程不可能又是joinable又是分离的;
        不论线程怎么折腾,只有线程出现异常,无论是否分离,进程都会出问题。
(5)测试代码:
//所有线程的pid都是进程的pid,均相同
#include 
#include 
#include 

void* thread_run(void* arg)//传入线程创建的函数
{
    const char* msg = (const char*)arg;
    printf("%s: tid:%#x, pid:%d\n", msg, pthread_self(), getpid());
    sleep(3);
    //pthread_detach(pthread_self());//自己分离自己                                                                                                                     
    //只要线程异常,不论是否分离,都会影响进程
    int *a;
    *a = 10;
}  
   
int main()
{   
    pthread_t tid;//无符号长整型
    pthread_create(&tid, NULL, thread_run, "thread 1");
    
    //分离后再join,就会出错
    pthread_detach(tid);
    
    while(1)
    {   
        printf("I am main thread\n");
        sleep(1);
    }
    ////sleep(2);
    //int ret = pthread_join(tid, NULL);//不关心线程退出信息
    //printf("ret: %d\n", ret);
    return 0;
}

运行结果:

        可以看到,即使我们分离了新线程,进程最后还是会出现段错误。
4. 线程终止

如果要终止线程而不终止进程,我们有三种办法:
(1)从线程函数调用return。这种方法对主线程不适用,因为从main函数调用return相当于进程退出;
(2)调用pthread_exit终止自己。不适用于main函数,main函数必须要以进程方式退出;

    1)函数原型:
线程创建,线程等待,线分离,线程终止_第5张图片
    2)函数功能:使得线程终止
    3)函数参数:
        retval: 该线程退出时的退出码,即该线程调用函数start_routine的返回值
注意:参数retval不要指向一个局部变量,因为当线程的调用函数退出时,局部变量也销毁了。
    4)函数返回值:无返回值,跟线程一样,线程自己终止自己时无法返回到它的调用者(自身)
    5)测试代码:
//线程的终止                                                                                                                                                            
#include 
#include 
#include 
#include 

void* thread_run(void* arg)//传入线程创建的函数
{
    //pthread_self函数用于获得当前线程id
    printf("I am %s, id is %lu\n", (char*)arg, pthread_self());
    pthread_exit((void*)99);
    //exit(123);//exit用于进程退出
}

int main()
{
    pthread_t tid;//无符号长整型
    //新线程去执行thread_run函数
    int ret = pthread_create(&tid, NULL, thread_run, "thread 1");//这里给thread_run传入的参数为一个字符串
    //这之后的是主线程在执行
        
    void* ptr;
    //这里阻塞式等待,获得线程退出信息
    pthread_join(tid, &ptr);
    printf("main thread run, new thread ret: %d\n", (int)ptr);
    return 12; 
}

运行结果:
(3)一个线程可以调用pthread_cancel函数终止同一进程中的另一线程

    1)函数原型:
线程创建,线程等待,线分离,线程终止_第6张图片
    2)函数功能:取消一个执行中的线程,尽量不要用于取消自己
    3)函数参数:
        thread:线程id
    4)函数返回值:成功返回0,失败返回错误码

    5)测试代码:
//线程的终止                                                                                                                                                            
#include 
#include 
#include 
#include 

void* thread_run(void* arg)//传入线程创建的函数
{
    //pthread_self函数用于获得当前线程id
    printf("I am %s, id is %lu\n", (char*)arg, pthread_self());
    pthread_cancel(pthread_self());//线程取消自己
    //sleep(3);
    //pthread_exit((void*)99);
    //exit(123);//exit用于进程退出
}

int main()
{
    pthread_t tid;//无符号长整型
    //新线程去执行thread_run函数
    int ret = pthread_create(&tid, NULL, thread_run, "thread 1");//这里给thread_run传入的参数为一个字符串
    //这之后的是主线程在执行
    
    //sleep(2);
    //pthread_cancel(tid);//取消线程
    
    void* ptr;
    //这里阻塞式等待,获得线程退出信息
    pthread_join(tid, &ptr);
    printf("main thread run, new thread ret: %d\n", (int)ptr);
    return 12; 
}

运行结果:
        若是在新线程的执行函数中取消自己,运行结果为:
        若是让主线程取消新创建的线程,运行结果为:
注意:

    1.pthread_exit或者return返回的指针所指向的内存单元必须是全局的或者是malloc分配的,不能在线程函数的栈上分配,因为当其他线程函数得到这个返回指针的时候线程函数已经退出了;
    2.exit用于进程退出,并不能用于线程退出;

                    


你可能感兴趣的:(Linux)