当前位置: 首页 > article >正文

Linux -- 线程控制相关的函数

目录

pthread_create -- 创建线程

参数

返回值

 代码 -- 不传 args:

编译时带  -lpthread

运行结果 

为什么输出混杂?

如何证明两个线程属于同一个进程?

 

如何证明是两个执行流? 

什么是LWP?

代码 -- 传 args:

运行结果:

pthread_self -- 线程标识符

代码:

LWP标识符 和 线程标识符的区别

pthread_join -- 等待线程退出

 前言:主线程比新线程先退出

参数

返回值

 代码 -- 不获取退出状态

代码 -- 获取退出状态

 ​编辑

pthread_exit -- 终止线程

前言:新、主线程共享地址空间

参数

作用

代码 

pthread_cancel -- 取消线程

参数

返回值

代码


pthread_create -- 创建线程

#include <pthread.h>

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

参数

thread: 指向一个 pthread_t 类型的指针,用于存储新创建线程的标识符,是输出型参数

attr: 指向一个 pthread_attr_t 类型的指针,这个参数可以用来设置线程的属性,如栈大小、调度策略等。如果不需要特别设置线程属性,可以传递 NULL

start_routine: 这是一个函数指针,指向新线程开始执行的函数。该函数必须接受一个 void* 类型的参数,并返回一个 void* 类型的结果。

arg: 这个参数将被传递给 start_routine 函数作为其唯一的参数。如果你不想传递任何参数,可以使用 NULL

返回值

如果函数调用成功,返回值为 0

如果发生错误,返回值为一个非零的错误代码

 代码 -- 不传 args:

#include<iostream>
#include<pthread.h>
#include<unistd.h>
using namespace std;

void* newthreadRun(void* args)
{
    while(1)
    {
        cout<<"I am new thread"<<endl;
        sleep(1);
    }
}
int main()
{
    pthread_t tid;
    pthread_create(&tid,nullptr,newthreadRun,nullptr);
    while(1)
    {
        cout<<"I am main thread"<<endl;
        sleep(1);
    }
    return 0;
}

编译时带  -lpthread

这里需要了解一点小故事,用户知道线程和进程,但不知道轻量级进程,而Linux中没有真线程,Linux中没有线程相关的系统调用,只有轻量级进程的系统调用,为了让用户和系统达成一致,系统将轻量级进程的系统调用进行封装,转换成线程相关的接口语义提供给用户,也就有了pthread库(即原生线程库),这个库既不属于C语言,也不属于C++,所以在Linux系统中编写多线程时,都必须在编译时带上 -lpthread。

在 Linux 中编译使用 Pthreads 的程序时,通常需要链接 Pthreads 库。这可以通过编译命令中添加 -lpthread 选项来实现。-lpthread 选项不仅告诉编译器链接 Pthreads 库,还会启用一些必要的编译器选项,以确保线程支持的正确性和性能。 如果不使用 -lpthread 选项,编译器可能会报错或生成不可用的二进制文件。

thread:thread.cc
	g++ -o $@ $^ -std=c++11 -lpthread

.PHONY:clean
clean:
	rm -f thread

运行结果 

可以看出,两个执行流同时输出信息。同时也可以看出,一开始新、主线程打印的消息混在一起了,后面才分开来,这是正常现象。

为什么输出混杂?

在多线程程序中,多个线程同时向终端输出信息时,可能会出现输出混杂的情况。这是因为每个线程的输出操作并不是原子的(原子操作,即要么完全执行,要么根本不执行),即一个线程可能在另一个线程已经开始输出但还没有完成输出时就开始了自己的输出,这种现象通常称为“输出交错”或“输出混杂”。 

如何证明两个线程属于同一个进程?

不传 args 版本的代码运行时,输入命令 ps ajx | head -1 && ps ajx | grep thread 筛选出 thread 进程,可以看出只有一个进程被调度

如何证明是两个执行流? 

当代码运行起来时,输入命令 ps -aL | head -1 && ps -aL | grep thread 可以查看线程的信息,可以看出两个线程的 pid 一样,即属于同一个进程,而 LWP 不同,则说明一个进程中有两个执行流

ps -aL-a显示所有进程,包括其他用户的进程,-L 显示每个线程的详细信息。 

什么是LWP?

LWP(Light Weight Process)是轻量级进程的缩写,在 Linux 中,LWP 通常被称为“线程”

代码 -- 传 args:

#include<iostream>
#include<pthread.h>
#include<unistd.h>
using namespace std;
#include<string>

string toHex(pthread_t tid)
{
    char buffer[64];
    snprintf(buffer,sizeof(buffer),"0x%lx",tid);
    return buffer;
}
void* newthreadRun(void* args)
{
    std:string threadname=(char*)args;
    int cnt=5;
    while(cnt--)
    {
       std::cout<<threadname<<" is running: "<<cnt<<", pid: "<<getpid()<<" mythread id: "<<toHex(pthread_self())<<std::endl;
       sleep(1);
    }
    return nullptr;
}
int main()
{
    pthread_t tid;
    pthread_create(&tid,nullptr,newthreadRun,(void*)"thread-1");
    
    pthread_join(tid,nullptr);
    return 0;
}

运行结果:

pthread_self -- 线程标识符

#include <pthread.h>
pthread_t pthread_self(void);

该函数用于获取当前线程的标识符pthread_t 类型)。

代码:

#include<iostream>
#include<pthread.h>
#include<unistd.h>
using namespace std;
#include<string>

string toHex(pthread_t tid)
{
    char buffer[64];
    snprintf(buffer,sizeof(buffer),"0x%lx",tid);
    return buffer;
}
void* newthreadRun(void* args)
{
    while(1)
    {
        cout<<"I am new thread, new thread tid: "<<toHex(pthread_self())<<", pid: "<<getpid()<<endl;
        sleep(1);
    }
}
int main()
{
    pthread_t tid;
    pthread_create(&tid,nullptr,newthreadRun,nullptr);
    while(1)
    {
        cout<<"I am main thread, main thread tid: "<<toHex(pthread_self())<<", pid: "<<getpid()<<endl;
        sleep(1);
    }
    return 0;
}

可以看出,新、主线程的线程标识符 tid 的值不一样,同时也可以看出,LWP 和线程标识符 tid的值是不一样的

LWP标识符 和 线程标识符的区别

 LWP(Light Weight Process)标识符和线程标识符(Thread Identifier,TID)在数值上通常是不一样的。虽然它们在概念上密切相关,但它们表示的是不同的标识符,用途和获取方式也有所不同。

pthread_join -- 等待线程退出

 前言:主线程比新线程先退出

#include<iostream>
#include<pthread.h>
#include<unistd.h>
using namespace std;
#include<string>

string toHex(pthread_t tid)
{
    char buffer[64];
    snprintf(buffer,sizeof(buffer),"0x%lx",tid);
    return buffer;
}
void* newthreadRun(void* args)
{
    std:string threadname=(char*)args;
    int cnt=5;//新线程运行5秒
    while(cnt--)
    {
       std::cout<<threadname<<" is running: "<<cnt<<", pid: "<<getpid()<<" mythread id: "<<toHex(pthread_self())<<std::endl;
       sleep(1);
    }
    return nullptr;
}
int main()
{
    pthread_t tid;
    pthread_create(&tid,nullptr,newthreadRun,(void*)"thread-1");
    
    sleep(1);//因为主线程没有阻塞等待新线程,1秒后,主线程先退出了
    std::cout<<"main thread quit"<<std::endl;
    return 0;
}

因为主线程没有阻塞等待新线程退出,1秒后,主线程退出了,主线程退出了就等同于整个进程退出了,分配给进程的资源都被释放了,所以所有的线程都要退出,所以新线程还没执行完就被退出了,通常需要主线程最后结束。线程的退出也需要wait,不然会发生内存泄漏问题。


#include <pthread.h>

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

该函数用于等待指定的线程终止,并获取该线程的退出状态。 

参数

thread:要等待的线程的标识符pthread_t 类型,可以调用 pthread_self 函数获取)。

value_ptr输出型参数,用于存储线程的退出状态。如果不需要获取退出状态,可以传递 NULL

返回值

等待线程退出成功返回 0。 

等待线程退出失败返回非零错误码

 代码 -- 不获取退出状态

#include<iostream>
#include<pthread.h>
#include<unistd.h>
using namespace std;
#include<string>

string toHex(pthread_t tid)
{
    char buffer[64];
    snprintf(buffer,sizeof(buffer),"0x%lx",tid);
    return buffer;
}
void* newthreadRun(void* args)
{
    std:string threadname=(char*)args;
    int cnt=5;
    while(cnt--)
    {
       std::cout<<threadname<<" is running: "<<cnt<<", pid: "<<getpid()<<" mythread id: "<<toHex(pthread_self())<<std::endl;
       sleep(1);
    }
    return nullptr;
}
int main()
{
    pthread_t tid;
    pthread_create(&tid,nullptr,newthreadRun,(void*)"thread-1");
    
    int n=pthread_join(tid,nullptr);//获取返回值
    std::cout<<"main thread quit, n="<<n<<std::endl;
   
    return 0;
}

新线程正常退出,故返回值为 0. 

代码 -- 获取退出状态

#include<iostream>
#include<pthread.h>
#include<unistd.h>
using namespace std;
#include<string>

string toHex(pthread_t tid)
{
    char buffer[64];
    snprintf(buffer,sizeof(buffer),"0x%lx",tid);
    return buffer;
}
void* newthreadRun(void* args)
{
    std:string threadname=(char*)args;
    int cnt=5;
    while(cnt--)
    {
       std::cout<<threadname<<" is running: "<<cnt<<", pid: "<<getpid()<<" mythread id: "<<toHex(pthread_self())<<std::endl;
       sleep(1);
    }
    return (void*)123;
}
int main()
{
    pthread_t tid;
    pthread_create(&tid,nullptr,newthreadRun,(void*)"thread-1");

    void* ret=nullptr;
    int n=pthread_join(tid,&ret);
    //ret强转为long long是为了避免精度损失
    std::cout<<"main thread quit, n="<<n<<",main thread get a ret:"<<(long long)ret<<std::endl;

    return 0;
}

线程的退出状态其实就是线程执行的任务函数的返回值。 

 

pthread_exit -- 终止线程

前言:新、主线程共享地址空间

#include<iostream>
#include<pthread.h>
#include<unistd.h>
using namespace std;
#include<string>
int g_val=100;
string toHex(pthread_t tid)
{
    char buffer[64];
    snprintf(buffer,sizeof(buffer),"0x%lx",tid);
    return buffer;
}
void* newthreadRun(void* args)
{
    std:string threadname=(char*)args;
    int cnt=5;
    while(cnt--)
    {
       printf("new thread, g_val:%d,&g_val:%p\n",g_val,&g_val);
       g_val++;//在新线程中改变g_val的值
       sleep(1);
    }
    return nullptr;
}

int main()
{
    pthread_t tid;
    pthread_create(&tid,nullptr,newthreadRun,(void*)"thread-1");

    int cnt=10;
    while(cnt--)
    {
        //主线程不改变g_val的值
        printf("main thread, g_val:%d,&g_val:%p\n",g_val,&g_val);
        sleep(1);
    }

    void* ret=nullptr;
    int n=pthread_join(tid,&ret);
    std::cout<<"main thread quit, n="<<n<<",main thread get a ret:"<<(long long)ret<<std::endl;
   
    return 0;
}

可以看出,新线程修改了 g_val 的值,主线程中 g_val 的值也被修改了,说明新、主线程共享了地址空间,看到的是同一个变量,而不是和进程一样,发生写时拷贝。

#include<iostream>
#include<pthread.h>
#include<unistd.h>
using namespace std;
#include<string>
int g_val=100;
string toHex(pthread_t tid)
{
    char buffer[64];
    snprintf(buffer,sizeof(buffer),"0x%lx",tid);
    return buffer;
}
void* newthreadRun(void* args)
{
    std:string threadname=(char*)args;
    int cnt=5;
    while(cnt--)
    {
       printf("new thread, g_val:%d,&g_val:%p\n",g_val,&g_val);
       g_val++;
       //故意对空指针进行解引用
       int *p=nullptr;
       *p=10;
       sleep(1);
    }
    return nullptr;
}

int main()
{
    pthread_t tid;
    pthread_create(&tid,nullptr,newthreadRun,(void*)"thread-1");

    int cnt=10;
    while(cnt--)
    {
        printf("main thread, g_val:%d,&g_val:%p\n",g_val,&g_val);
        sleep(1);
    }

    void* ret=nullptr;
    int n=pthread_join(tid,&ret);
    std::cout<<"main thread quit, n="<<n<<",main thread get a ret:"<<(long long)ret<<std::endl;
   
    return 0;
}

 

在新线程中故意对野指针进行解引用,结果新、主线程一起退出了,这是因为在同一个进程中运行的所有线程共享相同的地址空间,这意味着如果一个线程造成了段错误(segmentation fault),那么这个错误会影响到整个进程,而不仅仅是单个线程。操作系统通常会终止整个进程以防止进一步的损坏


#include <pthread.h>

void pthread_exit(void *value_ptr);

参数

value_ptr:一个指向指针的指针,用于存储线程的退出状态

这个值可以被 pthread_join 函数捕获并使用。如果不需要传递退出状态,可以传递 NULL

作用

终止当前线程:调用 pthread_exit 的线程会立即终止其执行

传递退出状态:可以通过 value_ptr 参数传递一个退出状态,这个状态可以被 pthread_join 函数捕获。

代码 

#include<iostream>
#include<pthread.h>
#include<unistd.h>
using namespace std;
#include<string>
int g_val=100;
string toHex(pthread_t tid)
{
    char buffer[64];
    snprintf(buffer,sizeof(buffer),"0x%lx",tid);
    return buffer;
}
void* newthreadRun(void* args)
{
    std:string threadname=(char*)args;
    int cnt=5;
    while(cnt--)
    {
       printf("new thread, g_val:%d,&g_val:%p\n",g_val,&g_val);
     
       g_val++;
       
       sleep(1);
    }
    pthread_exit((void*)123);
}

int main()
{
    pthread_t tid;
    pthread_create(&tid,nullptr,newthreadRun,(void*)"thread-1");

    void* ret=nullptr;
    int n=pthread_join(tid,&ret);
    std::cout<<"main thread quit, n="<<n<<",main thread get a ret:"<<(long long)ret<<std::endl;
   
    return 0;
}

pthread_cancel -- 取消线程

#include <pthread.h>

int pthread_cancel(pthread_t thread);

参数

thread:要取消的线程的标识符pthread_t 类型)。

返回值

取消线程成功,返回 0

取消线程失败,返回非零错误码

代码

#include<iostream>
#include<pthread.h>
#include<unistd.h>
using namespace std;
#include<string>
int g_val=100;
string toHex(pthread_t tid)
{
    char buffer[64];
    snprintf(buffer,sizeof(buffer),"0x%lx",tid);
    return buffer;
}
void* newthreadRun(void* args)
{
    std:string threadname=(char*)args;
    int cnt=5;
    while(cnt--)
    {
       printf("new thread, g_val:%d,&g_val:%p\n",g_val,&g_val);
       
       g_val++;
      
       sleep(1);
    }
    pthread_exit((void*)123);
}

int main()
{
    pthread_t tid;
    pthread_create(&tid,nullptr,newthreadRun,(void*)"thread-1");

    pthread_cancel(tid);

    void* ret=nullptr;
    int n=pthread_join(tid,&ret);
    std::cout<<"main thread quit, n="<<n<<",main thread get a ret:"<<(long long)ret<<std::endl;
   
    return 0;
}

线程的退出状态为 -1,表示线程被取消。


http://www.kler.cn/a/448398.html

相关文章:

  • WPF Binding 绑定
  • 计算机网络 八股青春版
  • 随手记:小程序兼容后台的wangEditor富文本配置链接
  • 【Windows版】opencv 和opencv_contrib配置
  • 电脑丢失dll文件一键修复的多种方法分析,电脑故障修复攻略
  • Ubuntu上如何部署Nginx?
  • 判断实例化或推断的时机
  • 东方财富股吧发帖与评论爬虫
  • 【多维DP】力扣3122. 使矩阵满足条件的最少操作次数
  • CTF知识集-文件上传
  • 联合物种分布模型(JSDM)与Hmsc包:群落生态学数据分析与预测技术
  • Android adb查看某个进程的总线程数
  • C语言的指针和java的引用有什么区别?
  • 3 需求分析
  • Windows装Docker至D盘/其他盘(最新,最准确,直接装)
  • 【Linux】常用命令大全
  • ubuntu 安装更新 ollama新版本
  • 网络地址转换NAT
  • DeepFaceLab技术浅析(三):自编码器模块
  • 浏览器对JSON格式数据的支持【超详解】
  • #渗透测试#漏洞挖掘#红蓝攻防#护网#sql注入介绍04-盲SQL注入(Blind SQL Injection)
  • upload-labs靶场保姆级攻略
  • Python使用队列加多线程处理数据
  • SSM 医院预约挂号系统 Vue 实现:开启智能医疗新征程
  • 如何设置浏览器不缓存网页
  • Fastjson <= 1.2.47 反序列化漏洞复现