Linux——多线程(四)

简介: Linux——多线程(四)

C++的线程库

C++当中有一个创建多线程的函数:


但是这里要注意:任何语言在Linux中要实现多线程,必定要使用pthread库。

C++11中的多线程,本质就是对pthread库的封装。

线程的分离

默认情况下,新创建的线程是joinable的,线程退出后,需要对其进行pthread_join操作,否则无法释放资源,从而造成系统泄漏。

如果不关心线程的返回值,join是一种负担,这个时候,我们可以告诉系统,当线程退出时,自动释放线程资源。

这种就是线程分离。

这是获得当前调用这个函数id的接口:

#include <iostream>
#include <pthread.h>
#include <cassert>
#include <string>
#include <vector>
#include <unistd.h>
#include <cstdlib>
using namespace std;
string changid(const pthread_t &pthread_id)
{
    char buffer[128];
    snprintf(buffer,sizeof(buffer),"0x%x",pthread_id);
    return buffer;
}
void* start_routine(void* args)
{
    string name = static_cast<const char*>(args);
    while(true)
    {
        cout << name << "running" << changid(pthread_self()) << endl;
        sleep(1);
    }
}
int main()
{
    pthread_t tid;
    pthread_create(&tid, nullptr, start_routine, (void*)"thread");
    cout << "main thread running... new thread id:" << changid(tid) <<endl;
    pthread_join(tid, nullptr);
    return 0;
}

然后来看线程分离的接口:

参数是线程id。

#include <iostream>
#include <pthread.h>
#include <cassert>
#include <string>
#include <cstring>
#include <vector>
#include <unistd.h>
#include <cstdlib>
using namespace std;
string changid(const pthread_t &pthread_id)
{
    char buffer[128];
    snprintf(buffer,sizeof(buffer),"0x%x",pthread_id);
    return buffer;
}
void* start_routine(void* args)
{
    string name = static_cast<const char*>(args);
    pthread_detach(pthread_self());//设置自己为分离状态
    int count = 5;
    while(count)
    {
        cout << name << "running" << changid(pthread_self()) << endl;
        count--;
        sleep(1);
    }
    return nullptr;
}
int main()
{
    pthread_t tid;
    pthread_create(&tid, nullptr, start_routine, (void*)"thread");
    string main_tid = changid(pthread_self());
    cout << "main thread running... new thread id:" << changid(tid) << "main thread id:" << main_tid << endl;
    sleep(2);//这里是为了防止新线程还没有进行分离主线程就已经开始等待了
    //如果线程设置了分离,这里就不能等待了
    int n = pthread_join(tid, nullptr);
    cout << "result" << n << ":" << strerror(n) << endl;//这里进行报错之后继续向下运行代码
    return 0;
}

如何理解每个线程都有自己独立的栈结构

首先了解一下线程库和轻量级进程的关系:

我们用户都是通过pthread库来创建线程的。

在原生线程库当中,我们用这些接口创建的线程别人也可以同时使用。(因为是共享库)

并且也需要对这些线程进行管理:

每个结构体对应一个轻量级的进程。

Linux的方案;用户级线程,这些属性在库中,内核提供线程执行流的调度。

Linux用户级线程:Linux内核轻量级进程 == 1:1

那么线程的id究竟是什么呢?

也就是说一旦线程结束,通过返回值就会传给共享区的TCB中。

这也能说明为什么每个线程都有自己的栈结构了。

主线程使用的栈是在主线程栈,其他线程的栈是在共享区。(其实也就是线程库当中)

那么什么是线程的局部存储呢?

之前创建过一个全局变量,证明两个线程都会共享这个变量。

如果在这个变量前面加上:__thread就可以将一个内置类型设置为线程局部存储。

也就是说给每个线程都来一份这个变量,两个线程在使用这个变量的时候互不影响。

如果以后给线程设置私有属性可以加上这个。

封装线程接口

这里就用Linux的线程接口来实现C++中的多线程部分功能。

#include <iostream>
#include <pthread.h>
#include <cassert>
#include <string>
#include <cstring>
#include <vector>
#include <unistd.h>
#include <cstdlib>
#include <memory>
using namespace std;
class Thread;//声明
class Context//上下文,相当于一个大号的结构体
{
public:
    Thread *this_;
    void* args_;
public:
    Context():this_(nullptr),args_(nullptr)
    {}
    ~Context()
    {}
};
class Thread
{
    typedef function<void* (void*)> func_t;
public:
    //这里需要加一个静态,因为不加静态就是类成员函数,还有一个隐藏的this指针,也就说明这等于前面有一个缺省参数
    //所以在类内创建线程,想让对应的线程执行方法需要在方法前面加一个static
    static void* start_routine(void* args)
    {
        //但是静态方法不能调用成员方法或者成员变量,这里可以设置一个上下文
        Context* ctx = static_cast<Context*>(args);
        void* ret = ctx->this_->run(ctx->args_);//这里让自身去调用这个方法
        delete ctx;
        return ret;
    }
    void* run(void* args)
    {
        return _func(args);//调用该函数
    }
    Thread(func_t func,void* args,int num):_func(func),_args(args)
    {
        char buffer[1024];
        snprintf(buffer, sizeof(buffer), "thread_%d", num);
        _name = buffer;
        Context* ctx = new Context();
        ctx->this_ = this;
        ctx->args_ = _args;//这里是将自身的部分数据传给ctx
        int n = pthread_create(&_tid, nullptr, start_routine, ctx);//这里要通过调用函数来转化,直接传func是不行的,因为类型是C++的类,不是C语言的类
        assert(n==0);
        (void)n;
    }
    void join()
    {
        int n = pthread_join(_tid,nullptr);
        assert(n==0);
        (void)n;
    }
    ~Thread()
    {}
private:
    string _name;//线程名字
    pthread_t _tid;//线程id
    func_t _func;//线程调用的函数
    void* _args;//传给函数的参数
};
#include "Thread.hpp"
void* thread_run(void* args)
{
    string work_type = static_cast<const char*>(args);
    while(true)
    {
        cout << "我是一个新线程:" << work_type <<endl;
        sleep(1);
    }
}
int main()
{
    unique_ptr<Thread> thread1(new Thread(thread_run, (void*)"hellothread",1));
    unique_ptr<Thread> thread2(new Thread(thread_run, (void*)"countthread",2));
    unique_ptr<Thread> thread3(new Thread(thread_run, (void*)"logthread",3));
    thread1->join();
    thread2->join();
    thread3->join();
    return 0;
}

相关文章
|
7月前
|
消息中间件 存储 缓存
【嵌入式软件工程师面经】Linux系统编程(线程进程)
【嵌入式软件工程师面经】Linux系统编程(线程进程)
140 1
|
5月前
|
算法 Unix Linux
linux线程调度策略
linux线程调度策略
110 0
|
3月前
|
资源调度 Linux 调度
Linux C/C++之线程基础
这篇文章详细介绍了Linux下C/C++线程的基本概念、创建和管理线程的方法,以及线程同步的各种机制,并通过实例代码展示了线程同步技术的应用。
49 0
Linux C/C++之线程基础
|
3月前
|
安全 Linux
Linux线程(十一)线程互斥锁-条件变量详解
Linux线程(十一)线程互斥锁-条件变量详解
|
5月前
|
存储 设计模式 NoSQL
Linux线程详解
Linux线程详解
|
5月前
|
缓存 Linux C语言
Linux线程是如何创建的
【8月更文挑战第5天】线程不是一个完全由内核实现的机制,它是由内核态和用户态合作完成的。
|
5月前
|
负载均衡 Linux 调度
在Linux中,进程和线程有何作用?
在Linux中,进程和线程有何作用?
|
5月前
|
缓存 Linux C语言
Linux中线程是如何创建的
【8月更文挑战第15天】线程并非纯内核机制,由内核态与用户态共同实现。
|
7月前
|
API
linux---线程互斥锁总结及代码实现
linux---线程互斥锁总结及代码实现
|
7月前
|
Linux API
Linux线程总结---线程的创建、退出、取消、回收、分离属性
Linux线程总结---线程的创建、退出、取消、回收、分离属性