Linux——多线程(线程概念|进程与线程|线程控制)

目录

地址空间和页表

如何看待地址空间和页表

虚拟地址如何转化到物理地址的

线程与进程的关系

什么叫进程?

什么叫线程?

如何看待我们之前学习进程时,对应的进程概念呢?和今天的冲突吗?

windows线程与linux线程

代码示例:

轻量级进程

线程的概念

举个例子(见见猪跑)

代码示例:

运行结果:

什么资源是线程私有的?

什么是线程?

cache刷新策略

一个线程如果出现了异常,会影响其他线程吗? 为什么?

给线程发信号

使用vfork函数

线程控制

线程的创建

pthread_create函数

创建多个线程

线程终止

线程的等待

概念

pthread_join函数

线程取消

概念

在C++中创建线程

分离线程

分离线程

pthread_detach函数

原生线程库

概念 

封装一个原生线程库

Thread.hpp

mythread.cc


地址空间和页表

如何看待地址空间和页表

1、地址空间是进程能看到的资源窗口
2、页表决定,进程真正拥有资源的情况
3、合理的对地址空间+页表进行资源划分,我们就可以对一个进程所有的资源进行分类。

虚拟地址如何转化到物理地址的

  1. 选择虚拟地址的前10个比特位在页目录当中进行查找,找到对应的页表。
  2. 再选择虚拟地址的10个比特位在对应的页表当中进行查找,找到物理内存中对应页框的起始地址。
  3. 最后将虚拟地址中剩下的12个比特位作为偏移量从对应页框的起始地址处向后进行偏移,找到物理内存中某一个对应的字节数据。

物理内存因为OS也要对其做管理,对物理内存进行了分页

struct Page{ // 内存的属性-- 4KB
}

一个个小块的物理内存为页框。

线程与进程的关系

线程:进程内的一个执行流

linux中是没有真正的线程的。

如果我们OS真的要专门设计“线程”概念,OS未来要不要管理这个线程呢?

肯定是要管理的!  先描述再组织!

一定要为线程设计专门的数据结构表示线程对象,TCB。--- Windows就是这样做的

单纯从线程调度角度,线程和进程有很多地方是重叠的!

所以,我们的linux工程师,不想给linux“线程”专门设计对应的数据结构!而是直接服用PCB!用PCB来表示linux内部的“线程”。

什么叫进程?

内核视角:承担分配系统资源的基本实体。

什么叫线程?

CPU调度的基本单位!

如何看待我们之前学习进程时,对应的进程概念呢?和今天的冲突吗?

1、linux内核中有没有真正意义的线程?没有的,linux是用进程PCB来模拟线程的,是一种完全属于自己的一套线程方案。
2、站在CPU的视角,每一个PCB,都可以称之为叫做轻量级进程
3、linux线程是CPU调度的基本单位,而进程是承担分配系统资源的基本单位
4、进程用来整体申请资源,线程用来伸手向进程要资源。
5、linux中没有真正意义的线程。

在进程中

状态、上下文切换、调度算法、包括进程与文件之间的对应关系、全部适用于线程!曾经我们为PCB编写的算法、数据结构是不是可以让线程复用并且可以复用进程的调度算法。

好处是什么?  

简单维护成本大大降低 -- 可靠性高效!

windows线程与linux线程

        linux无法提供创建线程的系统调用接口!而只能给我们提供创建轻量级进程的接口!

因为linux里面根本没有线程的概念,只有轻量级进程的。

        但Windows里面有具体的线程,并且有对应的线程控制块TCB。

        在Windows和Linux中,线程都是用于实现并发执行的基本单位。它们之间的主要区别在于线程的实现方式和调度机制。

        在Windows中,线程是由操作系统内核来管理和调度的,因此线程的创建和销毁等操作都需要系统调用。
    而在Linux中,线程是由用户空间的线程库来管理和调度的,因此线程的创建和销毁等操作都可以在用户空间中完成,不需要进行系统调用。

如何理解?举一个例子来说明!

家庭:  进程
家庭成员:   线程

代码示例:


#include <iostream>
#include <pthread.h>
#include <assert.h>
#include <unistd.h>

using namespace std;


// 新线程
void *thread_routine(void *args)
{   
    while(true)
    {
        cout<<"我是新线程,我正在运行!"<<endl;
        sleep(1);
    }

}

int main()

{

    pthread_t tid;
    int n=pthread_create(&tid,0,thread_routine\
    ,(void*)"thread one");
    assert(0==n);
    (void)n;


    // 主线程
    while(true)
    {
        cout<<"我是主线程,我正在运行!"<<endl;
        sleep(1);
    }




    return 0;
}

代码解释:

当kill 掉这个进程的时候,主线程和新线程都会被kill掉,因为kill是给进程发信号,当进程退出的时候会把对应的资源也给释放掉,因此所对应的线程不得不退出。

轻量级进程

查看轻量级进程的指令

ps -aL 

轻量级进程ID

LWP:(light weight process)轻量级进程ID

PID与LWP

PID与LWP相等的是主线程
PID与LWP不相等的是新线程


CPU调度的时候,是以哪一个id为标识符表示特定一个执行流的呢?

LWP

当内部只有一个执行流的时候,内部只有一个主线程。也可以将我们传入的线程名字给打印出来。

代码如下:


// 新线程
void *thread_routine(void *args)
{

    const char *name = (const char *)args;

    while (true)
    {
        cout << "我是新线程,我正在运行!"
             << "name:" << name << endl;
        sleep(1);
    }
}

运行结果:

[mwb@VM-16-14-centos lesson1]$ ./mythread 
我是主线程,我正在运行!
我是新线程,我正在运行!name:thread one
我是主线程,我正在运行!
我是新线程,我正在运行!name:thread one
我是主线程,我正在运行!
我是新线程,我正在运行!name:thread one
我是主线程,我正在运行!
我是新线程,我正在运行!name:thread one

线程的概念

举个例子(见见猪跑)

线程一旦被创建,几乎所有的资源都是被所有的线程所共享的。

代码示例:


#include <iostream>
#include <pthread.h>
#include <cstdio>
#include <assert.h>
#include <unistd.h>

using namespace std;

int g_val = 0;

string fun()
{
    return "我是一个独立的方法";
}


// 新线程
void *thread_routine(void *args)
{

    const char *name = (const char *)args;

    while (true)
    {
        cout << "我是新线程,我正在运行!"
             << "name:" << name << ": " << fun()<<" ";
        cout << "g_val: " << g_val++ << "&g_val: " << &g_val << endl;
        // fun();
        sleep(1);
    }
}

int main()

{

    pthread_t tid;
    int n = pthread_create(&tid, 0, thread_routine, (void *)"thread one");
    assert(0 == n);
    (void)n;

    // 主线程
    while (true)
    {
        char tidbuffer[64];
        snprintf(tidbuffer, sizeof(tidbuffer), "0x%x", tid);
        // cout << tidbuffer;
        cout << "我是主线程,我正在运行!,我创建出来的线程tid: "\
        << tidbuffer << ": " << fun()<<" ";
        cout << "g_val: " << g_val << "&g_val: " << &g_val << endl;

        // printf("0x%x",tid);
        fun();
        sleep(1);
    }

    return 0;
}

运行结果:


    
[mwb@VM-16-14-centos lesson1]$ ./mythread 
我是主线程,我正在运行!,我创建出来的线程tid: 0x9a4eb700: 我是一个独立的方法 g_val: 0&g_val: 0x6021f4
我是新线程,我正在运行!name:thread one: 我是一个独立的方法 g_val: 0&g_val: 0x6021f4
我是主线程,我正在运行!,我创建出来的线程tid: 0x9a4eb700: 我是一个独立的方法 g_val: 1&g_val: 0x6021f4
我是新线程,我正在运行!name:thread one: 我是一个独立的方法 g_val: 1&g_val: 0x6021f4
我是主线程,我正在运行!,我创建出来的线程tid: 0x9a4eb700: 我是一个独立的方法 g_val: 2&g_val: 0x6021f4
我是新线程,我正在运行!name:thread one: 我是一个独立的方法 g_val: 2&g_val: 0x6021f4
我是主线程,我正在运行!,我创建出来的线程tid: 0x9a4eb700: 我是一个独立的方法 g_val: 3&g_val: 0x6021f4
我是新线程,我正在运行!name:thread one: 我是一个独立的方法 g_val: 3&g_val: 0x6021f4

结论:

因此我们发现,两个线程共享了这个全局的函数。
并且同一个全局变量两个线程的地址一样,并且当有一个线程对该全局变量修改的时候,另一个线程也能看到   

什么资源是线程私有的?

Linux中线程私有的资源包括线程ID、寄存器、栈、errno变量和线程私有数据等。

线程ID:是一个唯一标识符,用于区分不同的线程。
寄存器:是CPU中的一组存储器,用于存储当前线程的状态信息。

栈:是用于存储函数调用和局部变量的一块内存区域,每个线程都有自己的栈。
errno变量:是用于存储系统调用错误码的全局变量,每个线程都有自己的errno变量。

线程私有数据:是一种机制,用于在不同的线程之间共享数据,每个线程都有自己的一份私有数据。

什么是线程?

在一个程序里的一个执行路线就叫做线程(thread)。

更准确的定义是:线程是“一个进程内部的控制序列”。

一切进程至少都有一个执行线程。
线程在进程内部运行,本质是在进程地址空间内运行。
在Linux系统中,在CPU眼中,看到的PCB都要比传统的进程更加轻量化。

透过进程虚拟地址空间,可以看到进程的大部分资源,将进程资源合理分配给每个执行流,就形成了线程执行流。

cache刷新策略

由于cache中缓存了很多热点数据,线程共享这片区域,所以cache不用更新

进程切换是需要cache刷新的。这是因为进程切换会导致CPU缓存中的数据无效,因此需要切换cache以确保新进程能够访问正确的数据。这个过程被称为“cache刷新”。

一个线程如果出现了异常,会影响其他线程吗? 为什么?

会的,健壮性/鲁棒性较差

代码示例:


#include <iostream>
#include <pthread.h>
#include <string>
#include <unistd.h>

using namespace std;

void *start_routine(void *args)
{
    // 一个线程如果出现了异常,会影响其他线程吗? 为什么?
    // 会的,健壮性/鲁棒性较差
    string name = static_cast<const char *>(args); // 安全的进行强制类型转化
    while (true)
    {
        cout << "new thread create success, name: " << name << endl;
        sleep(1);
        int *p=nullptr;
        *p=0;
    }
}

int main()
{

    pthread_t id;

    pthread_create(&id, nullptr, start_routine, (void *)"thread one");

    while (true)
    {
        cout << "new thread create success, name: main thread" << endl;
        sleep(1);
    }
}



运行结果:

[mwb@VM-16-14-centos lesson1]$ ./mythread 
new thread create success, name: main thread
new thread create success, name: thread one
new thread create success, name: main thread
Segmentation fault

给线程发信号

给所有PID相同的线程写入11号信号,因此所有的线程全部退出了。
进程信号,信号是整体发给进程的!

进程是承担系统分配资源的实体,当进程回收你赖以生存的资源的时候,线程肯定结束!

使用vfork函数

vfork创建一个子进程
创建出来的子进程是与父进程共享进程地址空间的。
也就相当于轻量级进程了。

线程控制

线程的创建

pthread_create函数

功能:

创建一个新的线程

函数原型:

int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void*), void *arg);

参数

thread:返回线程ID
attr:设置线程的属性,attr为NULL表示使用默认属性
start_routine:是个函数地址,线程启动后要执行的函数
arg:传给线程启动函数的参数,一般为线程名
返回值:成功返回0;失败返回错误码


错误码的解析:
 

传统的一些函数是,成功返回0,失败返回-1,并且对全局变量errno赋值以指示错误。

pthreads函数出错时不会设置全局变量errno(而大部分其他POSIX函数会这样做)。而是将错误代码通过返回值返回。

pthreads同样也提供了线程内的errno变量,以支持其它使用errno的代码。对于pthreads函数的错误,建议通过返回值判定,因为读取返回值要比读取线程内的errno变量的开销更小。

创建多个线程

代码示例:

#include <iostream>
#include <pthread.h>
#include <vector>
#include <string>
#include <unistd.h>

using namespace std;

// 创建一个结构体来存放线程的基本属性
class ThreadData
{
public:
    // 线程id
    pthread_t tid;
    // 线程的名字
    char namebuffer[64];
};

    // 创建新线程
//1. start_routine,现在是被几个线程执行呢? 10 ,这个函数是什么状态
// 2.  该函数是可重入函数吗?
void *start_routine(void *args)
{
    sleep(1);
    // 一个线程如果出现了异常,会影响其他线程吗? 为什么?
    // 会的,健壮性/鲁棒性较差
    ThreadData *td = static_cast<ThreadData *>(args); // 安全的进行强制类型转化
    int cnt = 10;
    while (cnt)
    {
        cout << "new thread create success, name: " << td->namebuffer
             << "cnt: " << cnt-- << endl;
        sleep(1);

    }
    delete td;
    return nullptr; //线程结束的,return的时候,线程就终止了
}

int main()
{
    // 1.我们想创建一批线程

    vector<ThreadData *> threads;
#define NUM 10

    for (int i = 0; i < NUM; i++)
    {
        ThreadData *td = new ThreadData();

        snprintf(td->namebuffer, sizeof(td->namebuffer), "%s:%2d", "thread", i+1);

        pthread_create(&td->tid, nullptr, start_routine, td);
        threads.push_back(td);
        // sleep(1);
    }

    for(auto &iter : threads)
    {
        cout<<"create thread: "<<iter->namebuffer<<" : "<<iter->tid<<"  success"<<endl;
    }

    while (true)
    {
        cout << "new thread create success, name: main thread" << endl;
        sleep(1);
    }

    return 0;
}

在函数内定义的变量,都叫做局部变量,具有临时性 -- 今天依旧适用-- 在多线程情况下,也没有问题 -- 其实每一个线程都有自己独立的栈结构!

线程终止

如何终止

exit是不能来终止线程的,而是用来终止进程的

任何一个执行流调用exit都会让整个进程退出

因此应该调用phtread_exit 

退出方法

1、return nullptr;
2、pthread_exit(nullptr);

线程的等待

概念

线程也是要被等待的!如果不等待,会造成类似僵尸进程的问题 -- 内存泄漏
线程必须也要被等待:
1、获取新线程的退出信息
2、回收新线程对应的PCB等内核资源,防止内存泄漏 -- 暂时无法查看!

那我们可以不关心退出信息吗?

可以。
但是我们是必须要回收资源的。

pthread_join函数

原型

int pthread_join(pthread_t thread,void **retval);

参数

retval :是一个输出型参数,用来获取线程函数结束时,返回的退出结果。

代码示例:目的是使用join来获取新线程退出后的退出信息


#include <iostream>
#include <pthread.h>
#include <vector>
#include <string>
#include<assert.h>
#include <unistd.h>

using namespace std;

// 创建一个结构体来存放线程的基本属性
class ThreadData
{
public:
    // 线程id
    pthread_t tid;
    // 线程的名字
    char namebuffer[64];
    // 线程的编号
    int number;
};
class ThreadReturn
{
public:
    int exit_code;
    int exit_result;
};



    // 创建新线程
//1. start_routine,现在是被几个线程执行呢? 10 ,这个函数是什么状态
//2.  该函数是可重入函数吗?
//3.在函数内定义的变量,都叫做局部变量,具有临时性 -- 今天依旧适用-- 在多线程情况下,也没有问题 -- 其实每一个线程都有自己独立的栈结构!

void *start_routine(void *args)
{
    // sleep(1);
    // 一个线程如果出现了异常,会影响其他线程吗? 为什么?
    // 会的,健壮性/鲁棒性较差
    ThreadData *td = static_cast<ThreadData *>(args); // 安全的进行强制类型转化
    int cnt = 5;
    while (cnt)
    {
        cout<<"cnt: "<<cnt<<" &cnt:"<<&cnt<<endl;
        cnt--;
        // cout << "new thread create success, name: " << td->namebuffer
        //      << "cnt: " << cnt-- << endl;
        // pthread_exit(nullptr);
        sleep(1);

    }
    
    ThreadReturn *tr=new ThreadReturn();
    tr->exit_code=1;
    tr->exit_result=106;

    return (void*)tr;// 右值
   

}


int main()
{
    // 1.我们想创建一批线程

    vector<ThreadData *> threads;
#define NUM 10

    for (int i = 0; i < NUM; i++)
    {
        ThreadData *td = new ThreadData();

        // 用来记录线程的编号
        td->number=i+1;

        snprintf(td->namebuffer, sizeof(td->namebuffer), "%s:%2d", "thread", i+1);

        pthread_create(&td->tid, nullptr, start_routine, td);
        threads.push_back(td);
       
    }

    for(auto &iter : threads)
    {
        cout<<"create thread: "<<iter->namebuffer<<" : "<<iter->tid<<"  success"<<endl;
    }

    for(auto &iter : threads)
    {   
        ThreadReturn *ret=nullptr;
        // void *ret=nullptr;// 注意:是void *
        int n= pthread_join(iter->tid,(void**)&ret);
        
        assert(0==n);
        
        
        cout<<"join : "<<iter->namebuffer << " success,exit_code: "<<\
        ret->exit_code<<", exit_result: "<<ret->exit_result<<endl;

        delete iter;
    }

    cout<<"main thread quit "<<endl;


    return 0;
}




为什么没有见到线程退出的时候,对应的退出信号??

线程出现异常,收到信号,整个进程都会退出!

pthread_join:默认就认为函数会调用成功!不考虑异常问题,异常问题是你进程该考虑的问题!

线程取消

概念

线程是可以被取消的!注意事项:线程要被取消,前提是这个线程已经跑起来了

取消也是线程终止的一种。


线程如果是被取消的,那么退出码为-1。

代码示例:线程的取消所使用函数:pthread_cancel
 




#include <iostream>
#include <pthread.h>
#include <vector>
#include <string>
#include <assert.h>
#include <unistd.h>

using namespace std;

// 创建一个结构体来存放线程的基本属性
class ThreadData
{
public:
    // 线程id
    pthread_t tid;
    // 线程的名字
    char namebuffer[64];
    // 线程的编号
    int number;
};
class ThreadReturn
{
public:
    int exit_code;
    int exit_result;
};

// 创建新线程
// 1. start_routine,现在是被几个线程执行呢? 10 ,这个函数是什么状态
// 2.  该函数是可重入函数吗?
// 3.在函数内定义的变量,都叫做局部变量,具有临时性 -- 今天依旧适用-- 在多线程情况下,也没有问题 -- 其实每一个线程都有自己独立的栈结构!

void *start_routine(void *args)
{
    
    ThreadData *td = static_cast<ThreadData *>(args); // 安全的进行强制类型转化
    int cnt = 5;
    while (cnt)
    {
        cout << "cnt: " << cnt << " &cnt:" << &cnt << endl;
        cnt--;
        
        sleep(1);
    }

    ThreadReturn *tr = new ThreadReturn();
    tr->exit_code = 1;
    tr->exit_result = 106;

    return (void *)tr; // 右值
    
}

int main()
{
    // 1.我们想创建一批线程

    vector<ThreadData *> threads;
#define NUM 10

    for (int i = 0; i < NUM; i++)
    {
        ThreadData *td = new ThreadData();

        // 用来记录线程的编号
        td->number = i + 1;

        snprintf(td->namebuffer, sizeof(td->namebuffer), "%s:%2d", "thread", i + 1);

        pthread_create(&td->tid, nullptr, start_routine, td);
        threads.push_back(td);
        // sleep(1);
    }

    for (auto &iter : threads)
    {
        cout << "create thread: " << iter->namebuffer << " : " << iter->tid << "  success" << endl;
    }

    // 线程是可以被cancel取消的,注意:线程被取消,前提是这个线程已经跑起来了
    // 线程如果是被取消的,那么退出码为-1 
    sleep(5);

    for (int i=0;i<threads.size()/2;i++)
    {
        pthread_cancel(threads[i]->tid);
        cout << "pthread_cancel :  " << threads[i]->tid << "  success" << endl;
    }

    for (auto &iter : threads)
    {
        void *ret = nullptr;
        
        assert(0 == n);
       

        cout << "join : " << iter->namebuffer << " success,exit_code: " <<(long long)ret<<endl;
        delete iter;
    }

    cout << "main thread quit " << endl;

    
    return 0;
}

在C++中创建线程


#include <iostream>
#include <thread>
#include <unistd.h>

void thread_run()
{
    while (true)
    {
        std::cout << "我是新线程..." << std::endl;
        sleep(1);
    }
}

int main()
{

    std::thread t1(thread_run);

    while(true)
    {
        std::cout<<"我是主线程..."<<std::endl;
        sleep(1);
    }

    t1.join();

    

    return 0;
}

结论:

任何语言,在Linux中如果要实现多线程,必定是要用pthread库
如何看待C++中的多线程呢?C++11的多线程,在linux环境中,本质是对pthread库的封装!

Linux中的线程称为用户级线程。

分离线程

线程是可以等待的、等待的时候,阻塞式等待。
如果我们不想等待呢?

分离线程

默认情况下,新创建的线程是joinable的,线程退出的时候,需要对其进行pthread_join操作,否则无法释放资源,从而造成系统泄露。
如果不关心线程的返回值,join是一种负担,这个时候,我们可以告诉系统,当线程退出时,自动释放线程资源。
可以用线程分离来实现。

使用pthread_self()可以获取当前线程的id。

代码示例:


#include <iostream>
#include <pthread.h>
#include <cstdio>
#include <unistd.h>

std::string changeId(const pthread_t &thread_id)
{
    char tid[128];
    snprintf(tid, sizeof(tid), "0x%x", thread_id);
    return tid;
}

void *start_routine(void *args)
{
    std::string threadname = static_cast<const char *>(args);

    while (true)
    {
        char tid[128];
        snprintf(tid, sizeof(tid), "0x%x", pthread_self());
        std::cout << threadname << "  running ... " << changeId(pthread_self()) << std::endl;
        sleep(1);
    }
}
int main()
{
    pthread_t tid;
    pthread_create(&tid, nullptr, start_routine, (void *)"thread one");

    std::string main_id = changeId(pthread_self());

    std::cout << "main  running ... new thread id:  " << changeId(tid) << "main thread id: " << main_id << std::endl;

    pthread_join(tid, nullptr);

    return 0;
}

代码结论:

可以获取主线程的线程id和新线程的线程id。
并且使用changeId来证明了我们获取新线程id的正确性。

pthread_detach函数

原型

int pthread_detach(pthread_t thread);

 代码示例:


#include <iostream>
#include <pthread.h>
#include <cstdio>
#include <unistd.h>
#include <cstring>

std::string changeId(const pthread_t &thread_id)
{
    char tid[128];
    snprintf(tid, sizeof(tid), "0x%x", thread_id);
    return tid;
}

void *start_routine(void *args)
{ 
    std::string threadname = static_cast<const char *>(args);

    int cnt=5;
    while (cnt--)
    {
        std::cout << threadname << "  running ... " << changeId(pthread_self()) << std::endl;
        sleep(1);
    }
}
int main()
{
    pthread_t tid;
    pthread_create(&tid, nullptr, start_routine, (void *)"thread one");

    std::string main_id = changeId(pthread_self());
    pthread_detach(tid);




    std::cout << "main  running ... new thread id:  " << changeId(tid) << "main thread id: " << main_id << std::endl;
    // sleep(5);
    // 一个线程默认是joinable的,如果设置了分离状态,就不能进行等待了
    int n = pthread_join(tid, nullptr);

    std::cout<<"result : "<<n<<": "<<strerror(n)<<std::endl;

    return 0;
}


原生线程库

概念 


原生线程库中,可能要存在多个线程--你用这些接口创建了线程,别人可以同时再用吗?
要不要对线程进行管理呢?

要!

先描述(线程的属性,比较少!),再组织!

线程的属性:

线程id,独立栈,线程的局部存储。


Linux的方案:

用户级线程,用户关心的线程属性在库中,内核提供线程执行流的调度。

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


线程id是该线程在线程库中该线程TCB的地址。独立栈在共享区的库中。


栈的分布。

主线程是用的自己地址空间中的栈。
其他线程用的是共享区中库中的栈。


线程的局部存储:

// 添加__thread,可以将一个内置类型设置为线程局部存储
__thread int g_val = 100;

可以让该变量放在共享区的栈上。


封装一个原生线程库

Thread.hpp

#pragma once

#include <iostream>
#include <pthread.h>
#include <functional>
#include <cassert>
#include <cstring>
#include <string>

class Thread;

// 上下文
class Context
{
public:
    Thread *this_;
    void *args_;
public:
    Context():this_(nullptr),args_(nullptr)
    {}
    ~Context()
    {}
};





class Thread
{
public:
    typedef std::function<void *(void *)> func_t;
    const int num = 1024;

public:
    Thread(func_t func, void *args, int number)
        : func_(func), args_(args)
    {

        // name_="thread-";
        // name_+=std::to_string(number);

        char buffer[num];
        snprintf(buffer, sizeof buffer, "thread-%d", number);
        name_ = buffer;
        
        // 异常 == if : 意料之外用异常或者if判断
        // assert:意料之中用assert

        Context *ctx=new Context();

        ctx->this_=this;
        ctx->args_=args_;



        int n = pthread_create(&tid_, nullptr, start_routine,ctx); // TODO
        assert(n == 0);
        (void)n;
        // 编译debug的方式发布的时候是存在的,release方式发布,
        // assert就不存在l,n就是一个定义,
        // 但是没有使用的变量,有些编译器下会有warning
    }

    // 在类内创建线程,想让线程执行对应的方法,需要将方法设置成为static
    // 类内成员,有缺省参数!在第一参数中包含了一个this指针
    static void *start_routine(void *args) 
    {
        Context *ctx=static_cast<Context*>(args);
        
        void *ret=ctx->this_->run(ctx->args_);

        delete ctx;

        return ret;

        // 静态方法不能调用成员方法或者成员变量
        // return func_(args_);

    }

   

    void join()
    {
        int n = pthread_join(tid_, nullptr);
        assert(n == 0);

        (void)n;
    }


    void *run(void *args)
    {
        return func_(args);
    }


    ~Thread()
    {
        // do northing
    }

private:
    std::string name_;
    func_t func_;
    void *args_;

    pthread_t tid_;
};

mythread.cc

#include <iostream>
#include <pthread.h>
#include <cstdio>
#include <unistd.h>
#include <memory>
#include <cstring>
#include "Thread.hpp"



void *thread_run(void *args)
{
    std::string work_type=static_cast<const char*>(args);

    while(true)
    {
        std::cout<<"我是一个新线程,我正在做:"<<work_type<<std::endl;
        sleep(1);
    }
}


int main()
{

    std::unique_ptr<Thread> thread1(new Thread(thread_run,(void*)"hellothread",1));
    std::unique_ptr<Thread> thread2(new Thread(thread_run,(void*)"countthread",2));
    std::unique_ptr<Thread> thread3(new Thread(thread_run,(void*)"logthread",3));
    

    thread1->join();
    thread2->join();
    thread3->join();




    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

袁百万

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值