Linux学习——线程的控制

news2024/11/17 21:22:13

目录

​编辑

一,线程的创建

二,线程的退出

1,在子线程内return

 2,使用pthread_exit(void*)

三,线程等待

四,线程获取自己的id值

五,线程取消

六,线程分离


一,线程的创建

在对进程控制之前,首先要做的便是创建一个线程。创建方法如下:

使用的创建方法叫做pthread_create。

参数介绍:

thread:线程id
attr:线程属性,直接设为null
start_routine:函数指针
arg:这个参数会传递进start_routinevoid*参数中。

例子:

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

 void* hander(void* args)//新线程执行的方法
 {
     while(true)
     {
       sleep(1);
       cout << "i am new thread" << endl;
     }
 }

 int main()
 {
   pthread_t td;
   pthread_create(&td, nullptr, hander, nullptr);//创建好新线程以后,新线程会去执行传入的hander方法。

   while(true)//主线程会继续向下执行自己的方法
   {
     sleep(1);
     cout << "i am main thread" << endl;
   }
   return 0;
 }

 执行这个代码以后结果如下:

在这里要注意,在使用g++编译时要加上-lpthread。因为线程库是一个第三方库,但是是安装在系统中的所以只需要-l便可以连接到pthread库

二,线程的退出

1,在子线程内return

  线程的退出有多种方式,先来看看最基本的一种退出方式,代码如下:

​
void *hander(void *args)
{
  string name = static_cast<const char *>(args);
  int cnt = 5;
  while (cnt--)
  {
    cout << "i am new thread" << name << endl;
    sleep(1);
  }
  return nullptr;//最基本的退出线程的方式便是直接在子线程内部使用return的方式退出
}

class data
{
public:
  char buf[64];
  int i;
};

int main()
{

  for (int i = 1; i <= NUM; i++) // 创建一批线程
  {
    data *m = new data();
    snprintf(m->buf, sizeof(m->buf), "%s:%d", "new thread", i);
    pthread_t td;
    pthread_create(&td, nullptr, hander, (void *)m->buf);
  }

  while (true)
  {
    cout << "-- -- -- -- -- -- -- --sucess -- -- -- -- -- -- -- " << endl;
    sleep(1);
  }
  return 0;
}

​

 在使用这种方式退出时,主线程在子线程退出以后还会继续执行。但是如果是子线程不退出而主线程先退出呢?像这样:

​
void *hander(void *args)
{
  string name = static_cast<const char *>(args);
  int cnt = 5;
  while (true)//子线程一直在死循环
  {
    cout << "i am new thread" << name << endl;
    sleep(1);
  }
  return nullptr;
}

class data
{
public:
  char buf[64];
  int i;
};

int main()
{

  for (int i = 1; i <= NUM; i++) // 创建一批线程
  {
    data *m = new data();
    snprintf(m->buf, sizeof(m->buf), "%s:%d", "new thread", i);
    pthread_t td;
    pthread_create(&td, nullptr, hander, (void *)m->buf);
  }

  int cnt = 5;
  while (cnt--)//主线程在cnt减到零时就退出
  {
    cout << "-- -- -- -- -- -- -- --sucess -- -- -- -- -- -- -- " << endl;
    sleep(1);
  }
  return 0;
}

​

 这样的话只要主线程退出了,这个进程都会直接结束。 如下:

 2,使用pthread_exit(void*)

这个函数是线程库提供给我们的专门用于线程退出的函数,他的参数可以直接设置为nullptr。使用方式如下:

void *hander(void *args)
{
  string name = static_cast<const char *>(args);
  int cnt = 5;
  while (cnt--)
  {
    cout << "i am new thread" << name << endl;
    sleep(1);
  }
  pthread_exit(nullptr);//使用pthread_exit()退出线程。
}

class data
{
public:
  char buf[64];
  int i;
};

int main()
{

  for (int i = 1; i <= NUM; i++) // 创建一批线程
  {
    data *m = new data();
    snprintf(m->buf, sizeof(m->buf), "%s:%d", "new thread", i);
    pthread_t td;
    pthread_create(&td, nullptr, hander, (void *)m->buf);
  }

  while (true)
  {
    cout << "-- -- -- -- -- -- -- --sucess -- -- -- -- -- -- -- " << endl;
    sleep(1);
  }
  return 0;
}

使用pthread_exit退出的效果和在子线程内使用return退出的效果一样。 

 ##注意##  线程的退出不能使用exit,因为exit的本质其实是向进程发信号,所以exit是专门用于进程退出的。同样的,线程的退出也不需要返回errno,因为如果一个线程因为异常退出的话整个进程都会退出,进程返回errno就可以了。

三,线程等待

和进程一样,线程也需要等待。等待的目的如下:

1,回收新线程对应的内核资源。

2,接收新线程返回的数据。

线程等待函数: int pthread_join(pthread_t thread, void **retval)

thread:表示要等待线程的pid

reval:接收数据并将数据带出。

使用如下:

class thread
{
public:
  int _num;       // 线程的编号
  char _buf[64];  // 线程的名字
  pthread_t _tid; // 线程的id
};

void *start_routine(void *args)
{

  int cnt = 5;
  while (cnt--)
  {
    sleep(1);
    thread *_td = static_cast<thread *>(args);
    cout << "i am new thread:" << _td->_buf << ":" << _td->_num
         << ":" << _td->_tid << endl;
  }

  pthread_exit(nullptr);//线程退出
}

int main()
{
  vector<thread*> threads;
  for (int i = 1; i <= 10; i++)//创建线程
  {
    thread *td = new thread;
    td->_num = i;
    snprintf(td->_buf, sizeof(td->_buf), "%s-%d", "thread:", i);
    pthread_create(&td->_tid, nullptr, start_routine, (void *)td);
    threads.push_back(td);
  }

  for(auto e:threads)
  {
    void *ret = nullptr;
    pthread_join(e->_tid, &ret);//回收线程
    cout << "等待成功"
         << " tid:" << e->_tid << endl;
  }

  cout << "等待结束" << endl;

  return 0;
}

以上的代码便演示了如何用pthread_join进行线程的等待效果如下:

那该函数里面的里面的返回值有什么作用呢?其实这个返回值就是用来带出退出码的。过程如下:

 添加打印退出码的信息以后结果如下:

那为什么reval的类型是二级指针类型呢?这其实是因为线程结束后,退出信息会写入到线程库内部。线程库内部的退出码便是void*类型的。此时我们要想的便是获取这个退出码了,如何获取呢?因为pthread_join()的返回值是int类型的,所以我们便不能直接让pthread_join()直接返回一个void*类型的变量,所以只能自己在用户层定义一个void*类型的retval然后retval的地址传入进去获取返回值了。

四,线程获取自己的id值

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

示例代码:

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

void *Done(void *args)
{
    uint64_t i = (uint64_t)args;

    string name = "thread_" + to_string(i);

    sleep(1);

    cout << name << "id :" << pthread_self() << endl;//使用pthread_self()打印线程id值。

    
}

int main()
{
    vector<pthread_t> wait;
    for (uint64_t i = 1; i <= 4; i++)
    {
        pthread_t td;
        pthread_create(&td, nullptr, Done, (void *)i); // 创建线程
        wait.push_back(td);

        sleep(2);
    }

    for (auto e : wait) // 等待线程
    {
       pthread_join(e, nullptr);
        
    }
    return 0;
}

 

如果用16进制打印便是下面这样的: 

其实线程的id就是一些地址。

五,线程取消

 进行线程取消的函数叫做pthread_cancel(pthread_t thread)。线程取消的前提是线程先运行起来,然后才能取消。

实验代码:创建线程,然后取消一半线程,观察现象。

class thread
{
public:
  int _num;       // 线程的编号
  char _buf[64];  // 线程的名字
  pthread_t _tid; // 线程的id
};

void *start_routine(void *args)
{

  int cnt = 5;
  while (cnt--)
  {
    sleep(1);
    thread *_td = static_cast<thread *>(args);
    cout << "i am new thread:" << _td->_buf << ":" << _td->_num
         << ":" << _td->_tid << endl;
  }

  pthread_exit((void*)100);
}

int main()
{
  vector<thread *> threads;
  for (int i = 1; i <= 10; i++)
  {
    thread *td = new thread;
    td->_num = i;
    snprintf(td->_buf, sizeof(td->_buf), "%s-%d", "thread:", i);
    pthread_create(&td->_tid, nullptr, start_routine, (void *)td->_buf);
    threads.push_back(td);
  }

  for (int i = 0;i<threads.size()/2;i++)//取消一半的线程
  {
    pthread_cancel(threads[i]->_tid);
  }

    for (auto e : threads)//等待
    {
      void *ret = nullptr;
      pthread_join(e->_tid, &ret);
      cout << "等待成功"
           << " tid:" << e->_tid << "quit code: " << (long long)(ret) << endl;

      delete e;
    }

  cout << "等待结束" << endl;

  return 0;
}

 运行结果如下:

可以看到如果取消线程,那线程还是会被等待然后退出,退出码是-1。其实这是一个宏:

六,线程分离

        线程分离使用到的函数 int pthread_detach(pthread_t thread)。先来说明一下,新创建的线程默认是joinable的。但是如果我的主线程并不关心当前的线程的返回值,那当前的线程便与我无关。那我的主线程去等待当前的线程便对我的主线程是一种负担。这个时候便可以来进行线程分离。线程的分离方式有两种:1,主线程去分离子线程    2,子线程自己进行分离。

示例代码:

1,主线程进行分离

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

void* Done(void* args)
{
    uint64_t i = (uint64_t)args;

    string name = "thread_" + to_string(i);

    int cnt = 5;
    while (cnt--)
    {
        sleep(1);
        cout << name << "running....." << endl;
        sleep(3);
    }
}

int main()
{
    vector<pthread_t> wait;
    for (uint64_t i = 1; i <= 4; i++)
    {
        pthread_t td;
        pthread_create(&td, nullptr, Done, (void*)i);//创建线程
        wait.push_back(td);
        sleep(3);//先休眠三秒,再进行线程分离
        pthread_detach(td);//主线程子集分离
    }

    for(auto e:wait)//等待线程
    {
       int n =  pthread_join(e,nullptr);
       cout << n << " " << endl;//打印等待的返回值,0表示成功,其它表示失败。
    }
        return 0;
}

2,子线程自己主动分离 

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

void *Done(void *args)
{
    uint64_t i = (uint64_t)args;

    string name = "thread_" + to_string(i);

    pthread_detach(pthread_self()); // 子线程自己自动分离

    int cnt = 5;
    while (cnt--)
    {
        cout << name << "running....." << endl;
        sleep(1);
    }
}

int main()
{
    vector<pthread_t> wait;
    for (uint64_t i = 1; i <= 4; i++)
    {
        pthread_t td;
        pthread_create(&td, nullptr, Done, (void *)i); // 创建线程
        wait.push_back(td);
    }

    for (auto e : wait) // 等待线程
    {
        int n = pthread_join(e, nullptr);
        cout << n << " " << endl; // 打印等待的返回值,0表示成功,其它表示失败。
    }
    return 0;
}

  

 

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

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

相关文章

MySQL常见的索引类型介绍

我将为您详细讲解 MySQL 中常见的索引类型&#xff0c;以及它们的使用场景、特点、区别和优势。索引是提高数据库查询性能的关键工具&#xff0c;它可以加速数据检索速度&#xff0c;减少服务器的负担。在 MySQL 中&#xff0c;索引类型主要包括 B-Tree 索引、哈希索引、全文索…

025—pandas 根多列判断不在其他列的数据

思路 是有两个相同结构的数据表&#xff0c;已知第二个表是第一个表的部分数据&#xff0c;需要以其中两列为单位&#xff0c;判断在第一个表中存在&#xff0c;在另外一个表中不存在的数据。 思路&#xff1a; 我们先将 df1 和 df2 的 x、y 列取出&#xff0c;组合为元组形成…

深入探索HAProxy:高性能负载均衡器的奥秘

目录 引言 一、HAProxy基础知识 &#xff08;一&#xff09;HAProxy概述 &#xff08;二&#xff09;核心特性 &#xff08;三&#xff09;支持调度算法 二、安装haproxy &#xff08;一&#xff09;下载源码包 &#xff08;二&#xff09;解决依赖环境 &#xff08;三…

教你实现微信公众号消息每日自动推送-俘获妹子芳心

教你实现微信公众号消息每日自动推送-俘获妹子芳心,我们使用的是github的这个库。链接:https://github.com/limoest/daily_reminder 。作者的仓库已经有足够详细的教程。本文主要描述搭我自己的建过程,帮助更多小白一步一步搭建实现~ 首先要fork该项目 申请微信接口公众测试…

高端竞赛活动,财会知识竞赛复赛方案

复赛环节共计有48名选手参与&#xff0c;随机分为2组&#xff0c;每组24名选手&#xff0c;经过与预赛环节相同的关卡及赛制&#xff0c;即每组选手皆通过两轮关卡赛一轮复活赛&#xff0c;每组角逐12名选手晋级。2组共计24名选手晋级决赛&#xff0c;而未进入决赛的选手则获得…

分享软件项目实施方案模板

本项目在实施过程中将遵守做到以下几个方面&#xff1a; 与建设单位共同完成整个系统软件、网络等设计,负责系统的开发、测试、调试、人员培训、系统的试运行和交付&#xff0c;并保证系统质量。负责系统的维护、应用软件的升级和更新。提出对系统硬件设备的相关技术要求。在项…

redis源码分析

是什么 是基于内存(而不是磁盘)的kv(而不是关系型mysql那种)数据库&#xff0c;通过空间换时间 源码分析 跳表skiplist 假设你有个有序链表&#xff0c;你想看某个特定的值是否出现在这个链表中&#xff0c;那你是不是只能遍历一次链表才能知道&#xff0c;时间复杂度为O(n…

JavaEE--SpringBoot配置⽂件

配置文件的基本概念 properties的语法格式 读取配置文件的内容 properties的缺点 yml 主要使用yml yml的文件格式 先举一个例子&#xff0c;简单了解一下 验证结果如下&#xff1a; yml读取配置文件的内容 配置文件内容为空的时候的情况 配置内容为null时的情况 配置…

算法项目(7)—— 文本检索图片

本文包含什么? 免环境配置,实现文本搜索图片全套代码以及代码介绍运行有问题? csdn上后台随时售后.项目说明 本文主要实现用文本搜索数据库中图片的功能. 项目运行 代码地址在文末 点击链接后选择T4的GPU: 将云盘中的代码文件clip_search.tar拖到下图位置上传代码: 执行…

OD_2024_C卷_200分_9、园区参观路径【JAVA】【动态规划】

package odjava;import java.util.Scanner;public class 九_园区参观路径 {public static void main(String[] args) {Scanner sc new Scanner(System.in);int n sc.nextInt(); // 长 -> 行数int m sc.nextInt(); // 宽 -> 列数int[][] matrix new int[n][m]; // 地图…

RK3568 xhci主控挂死问题

串口日志 rootjenet:~# [18694.115430] xhci-hcd xhci-hcd.1.auto: xHCI host not responding to stop endpoint command. [18694.125667] xhci-hcd xhci-hcd.1.auto: xHCI host controller not responding, assume dead [18694.125977] xhci-hcd xhci-hcd.1.auto: HC died; c…

SQLiteC/C++接口简介

上一篇&#xff1a;SQLite——世界上部署最广泛的开源数据库&#xff08;简介&#xff09; 引言&#xff1a; 作为一种轻量级、嵌入式关系型数据库&#xff0c;SQLite已经成为许多应用和系统的首选解决方案。它是一个开源软件库&#xff0c;以小型、快速和易于使用而著称。为…

利用yaml文件部署NacosXxl-job到阿里云的ACK

背景介绍 随着容器化的技术成熟落地&#xff0c;拥抱各种成熟的容器化集群平台是加速我们落地的必然之路&#xff0c;目前国内以阿里云、华为云、腾讯云为平台的供应商为主&#xff0c;国外则以AWS&#xff0c;Azure为主&#xff0c;让我们借助平台已有的优势进行快速落地提高…

zeta新增三个任务教程,即将刷新,速撸

这期是延续上期的教程&#xff0c;前面的任务不变&#xff0c;所以直接复制的前面的教程&#xff0c;多了三个任务更新在后面。 简单说一下&#xff0c;zeta是已经发币的公链项目&#xff0c;但是它社区还有6%的用户激励token份额没发完&#xff0c;做主网的几个简单任务可以获…

文本向量评测MTEB和C-MTEB

文章目录 简介MTEBC-MTEB参考资料 简介 MTEB(Massive Text Embedding Benchmark)是目前评测文本向量很重要的一个参考&#xff0c;其榜单也是各大文本向量模型用来展示与其他向量模型强弱的一个竞技台。 C-MTEB则是专门针对中文文本向量的评测基准。 MTEB MTEB的目的是为了…

基于51单片机超声波测距

目录 摘 要 2 ABSTRACT 3 目 录 4 1 绪论 1 1 概述 12 国内外发展现状 1 2 系统总体方案设计 21 设计要求 2 1&#xff09;可进行距离测量。 2 2&#xff09; 采用数码管显示距离数据。 2 3&#xff09; 可按键设置距离门限值 2 4&#xff09; 具有报警功能 22 方案选择 2 1 …

华为手机的备忘录怎么传到苹果15手机上?

曾经&#xff0c;我尝试过借助微信的复制粘贴功能&#xff0c;将备忘录内容一一传输。但这种方法既繁琐又容易出错&#xff0c;让我苦不堪言。我也曾试图通过数据线将数据导入电脑&#xff0c;再导入新手机。然而&#xff0c;这种方法不仅操作复杂&#xff0c;而且效率低下&…

脱围:使用 ref 保存值及操作DOM

♻️ 前面多篇文章中提及&#xff1a;state 可以 ① 保存渲染间的数据&#xff1b; ② state setter 函数更新变量会触发 React 重新渲染组件。 // 子组件&#xff1a;显示当前时间 function Time() {return (<p>{new Date().toLocaleString()}</p>) }export def…

伪分布HBase的安装与部署

1.实训目标 &#xff08;1&#xff09;熟悉掌握使用在Linux下安装伪分布式HBase。 &#xff08;2&#xff09;熟悉掌握使用在HBase伪分布式下使用自带Zookeeper。 2.实训环境 环境 版本 说明 Windows 10系统 64位 操作电脑配置 VMware 15 用于搭建所需虚拟机Linux系统 …

蜂窝物联:智慧水产养殖解决方案

一、系统介绍 集约化水产养殖水质在线监控系统是面向水产养殖集约、高产、高效、生态、安全的发展需求&#xff0c;基于智能传感、无线传感网、通信、智能处理与智能控制等物联网技术开发的&#xff0c;集水质环境参数在线采集、无线传输、智能处理、预警信息发布、决策支持、远…