C++多线程【Linux】

发布于:2025-06-27 ⋅ 阅读:(18) ⋅ 点赞:(0)

Linux的多线程

Linux的子线程实际上也是个进程,但是比传统的进程轻量化。

 

pthread

头文件<pthread.h>

线程ID:

ID类型为 pthread_t,是给usigned long int,

查看当前线程ID,调用如下函数:

pthread_t pthread_self(void)        //返回当前线程的线程ID

pthread_create 是 POSIX 线程库(pthread)中用于创建线程的函数,其语法如下:

#include <pthread.h>

int pthread_create(
    pthread_t *thread,           // 指向线程标识符的指针(输出参数)
    const pthread_attr_t *attr,  // 线程属性(NULL表示默认属性)
    void *(*start_routine)(void*), // 线程入口函数(返回void*,参数为void*)
    void *arg                   // 传递给入口函数的参数
);
// Compile and link with -pthread, 线程库的名字叫pthread, 全名: libpthread.so libptread.a
//注意:pthread的源代码是个动态库,在编译的时候要链接动态库

 代码:

#include <iostream>
#include <pthread.h>
#include <string>
void *callback(void *arg)
{
    std::cout << "Hello from thread!" <<pthread_self()<< std::endl;
    for(int i=0; i < 5; ++i)
    {
        std::cout << "Thread iteration: " << i << std::endl;
    }
    return nullptr;
}
int main()
{
    pthread_t tid;
    pthread_create(&tid,nullptr,callback,nullptr );
    std::cout<<"主线程:"<<pthread_self()<<std::endl;
    for (int i = 0; i < 5; i++)
    {
        std::cout<<i<<std::endl;
    }
    system("pause");
    return 0;
}

编译可执行文件:
g++ mythread.cpp -lpthread -o mythread
线程库文件(动态库),需要在编译的时候通过参数指定出来,动态库名为 libpthread.so需要使用的参数为 -l,根据规则掐头去尾最终形态应该写成:-lpthread(参数和参数值中间可以有空格)

线程退出

在编写多线程程序的时候,如果想要让线程退出,但是不会导致虚拟地址空间的释放(针对于主线程),我们就可以调用线程库中的线程退出函数,只要调用该函数当前线程就马上退出了,并且不会影响到其他线程的正常运行,不管是在子线程或者主线程中都可以使用。

#include <pthread.h>
void pthread_exit(void *retval);

参数: 线程退出的时候携带的数据,当前子线程的主线程会得到该数据。如果不需要使用,指定为NULL

#include <iostream>
#include <pthread.h>
#include <string>
void *callback(void *arg)
{
    std::cout << "Hello from thread!" <<pthread_self()<< std::endl;
    for(int i=0; i < 5; ++i)
    {
        std::cout << "Thread iteration: " << i << std::endl;
    }
    return nullptr;
}
int main()
{
    pthread_t tid;
    pthread_create(&tid,nullptr,callback,nullptr );
    std::cout<<"主线程:"<<pthread_self()<<std::endl;
    pthread_exit(NULL);
    return 0;
}

线程回收

线程函数
线程和进程一样,子线程退出的时候其内核资源主要由主线程回收,线程库中提供的线程回收函叫做pthread_join(),这个函数是一个阻塞函数,如果还有子线程在运行,调用该函数就会阻塞,子线程退出函数解除阻塞进行资源的回收,函数被调用一次,只能回收一个子线程,如果有多个子线程则需要循环进行回收。

另外通过线程回收函数还可以获取到子线程退出时传递出来的数据,函数原型如下:

#include <pthread.h>
// 这是一个阻塞函数, 子线程在运行这个函数就阻塞
// 子线程退出, 函数解除阻塞, 回收对应的子线程资源, 类似于回收进程使用的函数 wait()
int pthread_join(pthread_t thread, void **retval);

参数:
thread: 要被回收的子线程的线程ID

retval: 二级指针, 指向一级指针的地址, 是一个传出参数, 这个地址中存储了pthread_exit() 传递出的数据,如果不需要这个参数,可以指定为NULL

返回值:线程回收成功返回0,回收失败返回错误号。

#include <iostream>
#include <pthread.h>
#include <string>
struct Test
{
    int num;
    int age;
};
void *callback(void *arg)
{
    std::cout << "Hello from thread!" <<pthread_self()<< std::endl;
    for(int i=0; i < 5; ++i)
    {
        std::cout << "Thread iteration: " << i << std::endl;
    }
    struct Test *t = static_cast<struct Test *>(arg); //将void*转换为Test*
    t->num=100;
    t->age=6;

    pthread_exit(t);        //注意不要返回局部变量,不然有内存问题,这里的t指向的地址是主线程传入的地址
                            //如果t是局部变量,主线程无法访问到
    return nullptr;
}
int main()
{
    pthread_t tid;
        struct Test t;
    pthread_create(&tid,nullptr,callback,&t );
    std::cout<<"主线程:"<<pthread_self()<<std::endl;
    void *ptr;
    pthread_join(tid,&ptr);     //第二个参数,传入的是指针ptr的地址,是个二级指针,ptr最终指向callback函数的t
    struct Test * pt=static_cast<struct Test *>(ptr);
    std::cout << "Thread returned: num = " << pt->num << ", age="<<pt->age<<std::endl;    


    return 0;
}

注意pthread_exit(t)不要返回局部变量,不然有内存问题。这里的t指向的地址是主线程传入的地址;如果t是局部变量,子线程退出后,子线程栈区数据会被释放,主线程将无法访问到。

线程分离

在某些情况下,程序中的主线程有属于自己的业务处理流程,如果让主线程负责子线程的资源回收,调用pthread_join()只要子线程不退出主线程就会一直被阻塞,主要线程的任务也就不能被执行了。

在线程库函数中为我们提供了线程分离函数pthread_detach(),调用这个函数之后指定的子线程就可以和主线程分离,当子线程退出的时候,其占用的内核资源就被系统的其他进程接管并回收了。线程分离之后在主线程中使用pthread_join()就回收不到子线程资源了。

#include <pthread.h>
// 参数就子线程的线程ID, 主线程就可以和这个子线程分离了
int pthread_detach(pthread_t thread);

下面的代码中,在主线程中创建子线程,并调用线程分离函数,实现了主线程和子线程的分离:

 

#include <iostream>
#include <pthread.h>
#include <string>
struct Test
{
    int num;
    int age;
};
void *callback(void *arg)
{
    std::cout << "Hello from thread!" <<pthread_self()<< std::endl;
    for(int i=0; i < 5; ++i)
    {
        std::cout << "Thread iteration: " << i << std::endl;
    }
    struct Test t;    //在子线程中创建结构体t,因为主线程栈区数据在主线程结束后将访问不到
    t.num=100;            
    t.age=6;

    pthread_exit(&t);    
    return nullptr;
}
int main()
{
    pthread_t tid;

    pthread_create(&tid,nullptr,callback,nullptr); //创建线程,传入回调函数和参数
    std::cout<<"主线程:"<<pthread_self()<<std::endl;
    pthread_detach(tid); //分离线程,主线程不需要等待子线程结束
    return 0;
}

注意,线程分离后,如果callback参数是主线程栈区数据,将出现内存异常,子线程访问不到。

线程取消

线程取消的意思就是在某些特定情况下在一个线程中杀死另一个线程。使用这个函数杀死一个线程需要分两步:
1、在线程A中调用线程取消函数pthread_cancel,指定杀死线程B,这时候线程B是死不了的
2、在线程B中进程一次系统调用(从用户区切换到内核区),否则线程B可以一直运行。

#include <pthread.h>
// 参数是子线程的线程ID
int pthread_cancel(pthread_t thread);

 参数:要杀死的线程的线程ID
返回值:函数调用成功返回0,调用失败返回非0错误号。
在下面的示例代码中,主线程调用线程取消函数,只要在子线程中进行了系统调用,当子线程执行到这个位置就挂掉了。

#include <iostream>
#include <pthread.h>
#include <string>
struct Test
{
    int num;
    int age;
};
void *callback(void *arg)
{
    std::cout << "Hello from thread!" <<pthread_self()<< std::endl;
    for(int i=0; i < 5; ++i)
    {
        std::cout << "Thread iteration: " << i << std::endl;
    }
         struct Test t;
    t.num=100;
    t.age=6;
    return nullptr;
}
int main()
{
    pthread_t tid;

    pthread_create(&tid,nullptr,callback,nullptr); //创建线程,传入回调函数和参数
    std::cout<<"主线程:"<<pthread_self()<<std::endl;
    
        for(int i=0; i < 100; ++i)
    {
        std::cout << "主线程: " << i << std::endl;
    }
    pthread_cancel(tid);

    return 0;
}

关于系统调用有两种方式:
1、直接调用Linux系统函数
2、调用标准C库函数,为了实现某些功能,在Linux平台下标准C库函数会调用相关的系统函数

线程ID比较
在Linux中线程ID本质就是一个无符号长整形,因此可以直接使用比较操作符比较两个线程的ID,但是线程库是可以跨平台使用的,在某些平台上 pthread_t可能不是一个单纯的整形,这中情况下比较两个线程的ID必须要使用比较函数,函数原型如下:

#include <pthread.h>
int pthread_equal(pthread_t t1, pthread_t t2);

参数:t1 和 t2 是要比较的线程的线程ID
返回值:如果两个线程ID相等返回非0值,如果不相等返回0

C++线程类 

 


网站公告

今日签到

点亮在社区的每一天
去签到