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

news2024/12/23 10:46:56

目录

地址空间和页表

如何看待地址空间和页表

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

线程与进程的关系

什么叫进程?

什么叫线程?

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

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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/560455.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Leetcode665. 非递减数列

Every day a Leetcode 题目来源&#xff1a;665. 非递减数列 解法1&#xff1a;贪心 本题是要维持一个非递减的数列&#xff0c;所以遇到递减的情况时&#xff08;nums[i] > nums[i 1]&#xff09;&#xff0c;要么将前面的元素缩小&#xff0c;要么将后面的元素放大。 …

K8s in Action 阅读笔记——【2】First steps with Docker and Kubernetes

K8s in Action 阅读笔记——【2】First steps with Docker and Kubernetes 2.1 Creating, running, and sharing a container image 2.1.1 Installing Docker and running a Hello World container 在电脑上安装好Docker环境后&#xff0c;执行如下命令&#xff0c; $ dock…

真会玩:莫言用ChatGPT为余华写了一篇获奖词

5月16日&#xff0c;《收获》杂志65周年庆典暨新书发布活动在上海舞蹈中心举行。 典礼现场&#xff0c;余华凭借《文城》获得收获文学榜2021年长篇小说榜榜首。 作为老友&#xff0c;莫言在颁奖时故意卖了个关子&#xff1a;“这次获奖的是一个了不起的人物&#xff0c;当然了&…

OMA通道-2

1 简介 本文档中指定的 API 使移动应用程序能够访问移动设备中的不同 SE&#xff0c;例如 SIM 或嵌入式 SE。 本规范提供了接口定义和 UML 图&#xff0c;以允许在各种移动平台和不同的编程语言中实现。 如果编程语言支持命名空间&#xff0c;则它应为 org.simalliance.openmob…

Foxit PDF SDK OCR Add-on Library (C++, Windows)-Crk

OCR文档扫描--Crack version 使用Foxit PDF SDK OCR Add-on的光学字符识别&#xff08;OCR&#xff09;软件将扫描的文档转换为可搜索的文本PDF。专为扫描、归档和数字化而设计&#xff0c;我们的插件输出13种不同的文件格式&#xff0c;包括PDF和PDF/A。 在不投资数据输入人员…

Linux 禁用23端口

禁用23端口 前言 23端口是用于Telnet服务的默认端口。Telnet是一种早期的网络协议&#xff0c;允许用户使用一个远程终端连接到远程计算机上&#xff0c;以便在远程计算机上执行命令和操作。通过输入用于Telnet服务器的IP地址和端口号&#xff0c;用户可以在本地计算机上打开一…

【Java|golang】1090. 受标签影响的最大值---关联数组排序问题以及切片排序失败

我们有一个 n 项的集合。给出两个整数数组 values 和 labels &#xff0c;第 i 个元素的值和标签分别是 values[i] 和 labels[i]。还会给出两个整数 numWanted 和 useLimit 。 从 n 个元素中选择一个子集 s : 子集 s 的大小 小于或等于 numWanted 。 s 中 最多 有相同标签的 …

数据结构初阶--栈和队列OJ题

目录 前言有效的括号思路分析代码实现 用队列实现栈思路分析代码实现 用栈实现队列思路分析代码实现 设计循环队列思路分析代码实现 前言 本篇文章将对部分栈和队列综合运用题进行讲解&#xff0c;以对栈和队列有一个更深层次的理解。 有效的括号 先来看题 思路分析 这里…

优秀的流程图应该怎么设计呢?

优秀的流程图应该怎么绘制呢&#xff1f; 本文将带大家学习优秀流程图的绘制要点和技巧&#xff0c;以及讲解流程图与UML活动图、BPMN图之间的关系和区别。 1、认识流程图 流程图简单讲就是用图描述流程&#xff0c;这种流程可以是一种有先后顺序的操作组成&#xff0c;可以…

2024王道数据结构考研丨第六篇:查找、排序

到此&#xff0c;2024王道数据结构考研笔记专栏“基础知识”部分已更新完毕&#xff0c;其他内容持续更新中&#xff0c;欢迎 点此 订阅&#xff0c;共同交流学习… 文章目录 第七章 查找7.1查找表相关概念 第八章 排序8.1排序的基本概念8.2 插入排序8.2.1直接插入排序8.2.2折半…

使用Maven管理项目、导入依赖、测试打包项目、常用依赖

使用Maven管理项目 文章目录 使用Maven管理项目Maven项目结构Maven依赖导入Maven依赖作用域Maven可选依赖Maven排除依赖Maven继承关系Maven常用命令Maven测试项目Maven打包项目 Maven 翻译为"专家"、“内行”&#xff0c;是 Apache 下的一个纯 Java 开发的开源项目。…

hive函数03

自定义函数 Hive 自带了一些函数&#xff0c;比如&#xff1a;max/min等&#xff0c;但是数量有限&#xff0c;自己可以通过自定义UDF来方便的扩展。 在企业中处理数据的时候&#xff0c;对于敏感数据往往需要进行脱敏处理。比如手机号。我们常见的处理方式是将手机号中间4位…

MySQL表设计原则

前言 这里简单整理一些常用的数据库表设计原则以及常用字段的使用范围。 表的设计准则 1、命名规范 表名、字段名必须使用小写字母或者数字&#xff0c;禁止使用数字开头&#xff0c;禁止使用拼音&#xff0c;并且一般不使用英文缩写。主键索引名为 pk_字段名&#xff1b;唯…

SSL/TLS认证握手过程

一: SSL/TLS介绍 什么是SSL,什么是TLS呢&#xff1f;官话说SSL是安全套接层(secure sockets layer)&#xff0c;TLS是SSL的继任者&#xff0c;叫传输层安全(transport layer security)。说白点&#xff0c;就是在明文的上层和TCP层之间加上一层加密&#xff0c;这样就保证上层信…

ACP(MaxCompute篇)-MaxCompute开发工具

创建MaxCompute项目 第一种创建项目方式 1.知道MaxCompute服务。 2.创建项目。 3.创建成功。 第二种创建项目的方式 1.进入DataWorks控制台。 2.创建工作空间。 3.创建的类型。 4.创建计算方式。 5.自定义选择。 6.创建成功。 MaxCompute开发工具简介 Odpscmd 安装配置 下…

java boot项目认识一下三种格式的配置文件

之前我们在 application.properties 中写了很多配置 但boot并不是只有这种配置方式 boot提供了三种配置方式给我们 话不多说 直接上代码 我们先将 resources下的 application.properties给他干掉 然后在下面创建一个 application.yml 在下面编写代码如下 server:port: 81这…

Hystrix底层核心原理

1、Hystrix资源隔离技术 hystrix github 官方文档&#xff1a;Home Netflix/Hystrix Wiki GitHub hystrix可以完成隔离、限流、熔断、降级这些常用保护功能。 hystrix的隔离分为线程池隔离和信号量隔离 1.1、信号量隔离 信号量隔离就是hystrix的限流功能。虽然名字叫隔离…

企业应该如何选择一个靠谱的软件测试供应商?

人们的生活越来越离不开软件产品&#xff0c;随着选择越多&#xff0c;产品质量愈发重要&#xff0c;因此企业选择一个靠谱的软件测试供应商是一项关键任务&#xff0c;因为测试结果将直接影响到产品的质量、用户的体验和公司的声誉。以下是一些选择靠谱的软件测试供应商的技巧…

C++常用的支持中文的GUI库Qt 6之二:项目的结构、资源文件的使用

C常用的支持中文的GUI库Qt 6之二&#xff1a;项目的结构、资源文件的使用 上一篇Qt 6的下载、安装与简单使用https://mp.csdn.net/mp_blog/creation/editor/130730203&#xff0c;本文介绍Qt 6的项目的结构、资源文件的使用与发布。 基础 这一部分&#xff0c;初学时不明白是…

交通 | 考虑网络效应的共享出行差异化定价

封面图来源&#xff1a; https://www.pexels.com/zh-cn/photo/210182/ 编者按&#xff1a; 本文考虑了单程式共享汽车的定价问题&#xff0c;在考虑顾客需求网络效应以及实现影响的场景下&#xff0c;根据空间以及时间确定汽车租赁的单价以实现系统利润最大化。 1.引言 在过…