【Linux操作系统】线程控制

news2024/11/25 11:33:06

文章目录

  • 线程创建
  • 线程等待
  • 终止线程
  • 利用多线程求和(单进程多线程)
  • 获取线程ID
  • 取消线程
  • 线程分离
  • 共享?


线程创建

创建线程需要用的函数是pthread_create。函数原型如下:

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

参数说明

  • thread:获取创建成功的线程ID,该参数是一个输出型参数。
  • attr:用于创建线程的属性,传入NULL表示使用默认属性。
  • start_routine:该参数是一个函数地址,表示线程例程,即线程启动后要执行的函数。
  • arg:传给线程例程的参数。

创建线程代码示例:以下有一个主线程,并创建了一个新线程,主线程打印了pthread_create函数的第一个输出型参数,即新线程的id。
在这里插入图片描述
运行结果
在这里插入图片描述

演示:以下代码有一个主线程,利用snprintf对每个新线程进行了命名,并通过pthread_create函数创建了10个新线程,同时给thread_run函数传了参数tname。

在这里插入图片描述
运行结果:

在这里插入图片描述

线程等待

线程和进程一样,也是需要等待的,否则就会类似产生“僵尸进程”,产生内存泄漏的问题。等待线程用的是pthread_join函数,函数原型如下:

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

参数说明

  • thread:被等待线程的ID。
  • retval:线程退出时的退出码信息。
  • 线程等待成功返回0,失败返回错误码。

注意:pthread_join属于阻塞等待。

代码示例

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

终止线程

如果需要只终止某个线程而不是终止整个进程,有三种方法:

  • 从线程函数return。
  • 线程可以调用pthread_exit函数终止自己。
  • 一个线程可以调用pthread_cancel函数终止同一进程中的另一个线程。

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

利用多线程求和(单进程多线程)

  1 #include<iostream>
  2 #include<string>
  3 #include<unistd.h>
  4 #include<pthread.h>
  5 #include<ctime>
  6 
  7 #define NUM 10
  8 
  9 using namespace std;
 10 
 11 enum
 12 {
 13     OK=0,
 14     ERROR
 15 };
 16 
 17 class ThreadData
 18 {
 19 public:
 20     ThreadData(const string& name,int id,time_t createTime,int top)
 21     :_name(name)
 22     ,_id(id)
 23     ,_createTime((uint64_t)createTime)
 24     ,_status(OK)
 25     ,_top(top)
 26     ,_result(0)
 27     {}
 28     ~ThreadData()
 29     {}
 30 public:
 31     //输入的
 32     string _name;//线程名
 33     int _id;//线程id
 34     uint64_t _createTime;//线程创建时间
 35 
 36     //返回的
 37     int _status;//线程状态
 38     int _top;//求和至哪个整数
 39     int _result;//返回求和结果
 40 };
 41 void *thread_run(void *args)                                                                                                                                                     
 42 {
 43     ThreadData *td = static_cast<ThreadData *>(args);
 44 
 45     for(int i = 1; i <= td->_top; i++)
 46     {
 47         td->_result += i;
 48     }
 49     cout << td->_name << " cal done!" << endl;
 50     return td;
 51                                                                                                                                                                                  
 52     // while (true)
 53     // {
 54     //     cout << "thread is running, name " << td->_name << " create time: " << td->_createTime << " index: " << td->_id << endl;
 55     //     // // exit(10); // 进程退出,不是线程退出,只要有任何一个线程调用exit,整个进程(所有线程)全部退出!
 56     //     // sleep(4);
 57         
 58     //     // break;
 59     // }
 60     // delete td;
 61 
 62     // pthread_exit((void*)2);   // void *ret = (void*)1;
 63     // return nullptr;
 64 }
 65 int main()
 66 {
 67 
 68     //1.创建线程
 69     pthread_t tids[NUM];
 70 
 71     for(int i=0;i<NUM;i++)
 72     {
 73         char tname[64];
 74         //1.1将字符串格式化进tname
 75         snprintf(tname,64,"thread-%d",i+1);
 76         //1.2创建类对象:线程名,线程id,线程创建的时间戳,top表示求和到哪个整数
 77         ThreadData* td = new ThreadData(tname,i+1,time(nullptr),100+5*i);
 78 
 79         //1.3
 80         //thread:获取创建成功的线程ID,该参数是一个输出型参数。
 81         //创建线程的属性,传入NULL表示使用默认属性
 82         //线程启动后要执行的函数,这里用的是thread_run函数
 83         //传给线程例程的参数,我们自定义了一个求和整数的类对象
 84         pthread_create(tids+i,nullptr,thread_run,td);//创建线程,td是thread_run函数的参数,
 85         //同时td也是我们自己创建的类对象
 86         sleep(1);
 87     }
 88 
 89     //2.等待线程
 90     void *ret = nullptr;
 91     for(int i=0;i<NUM;i++)
 92     {
 93         //int pthread_join(pthread_t thread, void **retval);,注意是void**类型的
 94         //被等待线程的id,获取线程退出码
 95         int n = pthread_join(tids[i],&ret);
 96 
 97         if(n!=0)
 98         {
 99             cerr << "pthread_join error" << endl;
100         }
101         //获取线程退出码,将ret强制转换成ThreadData*
102         ThreadData* td = static_cast<ThreadData*>(ret);
103 
104         if(td->_status == OK)
105         {
106             cout << td->_name << " 计算的结果是: " << td->_result << " (它要计算的是[1, " << td->_top << "])" <<endl;
107         }
108         delete td;
109     }
110     cout << "all thread quit..." << endl;
111     return 0;
112 }

运行结果:
在这里插入图片描述

获取线程ID

常见获取线程ID的方式有两种:

  1. 创建线程时通过输出型参数获得。
  2. 通过调用pthread_self函数获得。调用pthread_self函数即可获得当前线程的ID,类似于调用getpid函数获取当前进程的ID。

pthread_self函数原型如下:

pthread_t pthread_self(void);

取消线程

pthread_cancel函数可以取消线程,pthread_cancel函数的函数原型如下:

int pthread_cancel(pthread_t thread);//thread是被取消线程的ID。

代码实例:

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

using namespace std;

void* thread_run(void* args)
{
    const char* name = static_cast<const char*>(args);

    int cnt=5;
    while(cnt)
    {
        cout<<name<<"is running"<<cnt--<<"获取该线程id"<<pthread_self()<<endl;
        sleep(1);
    }
    //5s后就终止线程
    pthread_exit((void*)11);
}

int main()
{
    pthread_t tid;
    //创建单进程单线程
    pthread_create(&tid,nullptr,thread_run,(void*)"thread1");

    sleep(3);//睡眠3s就取消线程

    pthread_cancel(tid);

    void* ret=nullptr;
    //等待线程
    pthread_join(tid,&ret);
    cout<<"new thread exit:"<<(int64_t)ret<<"退出线程"<<tid<<endl;
    return 0;
}

在这里插入图片描述
线程是可以取消自己的,取消成功的线程的退出码一般是-1。

线程分离

线程分离的函数:

int pthread_detach(pthread_t thread);//thread是所要分离线程的ID。
  • 默认情况下,新创建的线程是joinable(需要主线程等待回收资源的)的,线程退出后,需要对其进行pthread_join操作,否则无法释放资源,从而造成内存泄漏。
  • 但如果我们不关心线程的返回值,join也是一种负担,此时我们可以将该线程进行分离,后续当线程退出时就会自动释放线程资源。这里的意思是不再需要主线程去join了,当这个线程退出时系统会自动回收该线程所对应的资源。
  • 可以是线程组内其他线程对目标线程进行分离,也可以是线程自己分离。
  • 线程分离之后,就不能join了,不然会报错。
  • joinable和分离是冲突的,一个线程不能既是joinable又是分离的。

代码示例
创建五个新线程后让这五个新线程将自己进行分离,主线程就不需要在对这五个新线程进行join了,当这5个线程退出时系统会自动回收该线程所对应的资源。

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>

void* Routine(void* arg)
{
	pthread_detach(pthread_self());
	char* msg = (char*)arg;
	int count = 0;
	while (count < 5){
		printf("I am %s...pid: %d, ppid: %d, tid: %lu\n", msg, getpid(), getppid(), pthread_self());
		sleep(1);
		count++;
	}
	pthread_exit((void*)6666);
}
int main()
{
	pthread_t tid[5];
	for (int i = 0; i < 5; i++){
		char* buffer = (char*)malloc(64);
		sprintf(buffer, "thread %d", i);
		pthread_create(&tid[i], NULL, Routine, buffer);
		printf("%s tid is %lu\n", buffer, tid[i]);
	}
	while (1){
		printf("I am main thread...pid: %d, ppid: %d, tid: %lu\n", getpid(), getppid(), pthread_self());
		sleep(1);
	}
	return 0;
}

共享?

  • 全局变量是所有线程共享的,如果不想它被所以线程共享,需要加上__thread修饰变量。
  • 线程中的局部变量不是所有线程共享的。

同一线程内的局部变量地址是一致的,不同线程的局部变量是不共享的。

代码示例:

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

using namespace std;


void *threadRoutine(void* args)
{
    string name = static_cast<const char*>(args);
    int cnt = 5;
    while(cnt)
    {
        cout << name << " : " << cnt-- << " : " << pthread_self() << " &cnt: " << &cnt << endl;
        //cout << name << " g_val: " << g_val++ << ", &g_val: " << &g_val << endl;
        sleep(1);
    }
    return nullptr;
}

int main()
{
    //线程创建
    pthread_t t1,t2,t3;
    pthread_create(&t1,nullptr,threadRoutine,(void*)"thread 1");
    pthread_create(&t2,nullptr,threadRoutine,(void*)"thread 2");
    pthread_create(&t3,nullptr,threadRoutine,(void*)"thread 3");

    //线程等待
    pthread_join(t1,nullptr);
    pthread_join(t2,nullptr);
    pthread_join(t3,nullptr);

    return 0;
}

在这里插入图片描述

一个进程内的全局变量是所有线程共享的。

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

using namespace std;

int g_val = 100;
// std::string hexAddr(pthread_t tid)
// {
//     g_val++;
//     char buffer[64];
//     snprintf(buffer, sizeof(buffer), "0x%x", tid);

//     return buffer;
// }
void *threadRoutine(void* args)
{
    string name = static_cast<const char*>(args);
    int cnt = 5;
    while(cnt)
    {
        //cout << name << " : " << cnt-- << " : " << pthread_self() << " &cnt: " << &cnt << endl;
        cout << name << " g_val: " << g_val++ << ", &g_val: " << &g_val << endl;
        sleep(1);
    }
    return nullptr;
}

int main()
{
    pthread_t t1, t2, t3;
    pthread_create(&t1, nullptr, threadRoutine, (void*)"thread 1"); // 线程被创建的时候,谁先执行不确定!
    pthread_create(&t2, nullptr, threadRoutine, (void*)"thread 2"); // 线程被创建的时候,谁先执行不确定!
    pthread_create(&t3, nullptr, threadRoutine, (void*)"thread 3"); // 线程被创建的时候,谁先执行不确定!

    pthread_join(t1, nullptr);
    pthread_join(t2, nullptr);
    pthread_join(t3, nullptr);

   
    return 0;
}

在这里插入图片描述

用__thread修饰全局变量,这个全局变量就不会被所以线程共享了。

在这里插入图片描述

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

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

相关文章

left join 和except方法区别和联系

目录 相同点&#xff1a; left join except 不同点 假设有两个表&#xff1a;A客户表 和 B客户表&#xff0c;客户uid是唯一主键 相同点&#xff1a; 查询在A中的客户 但不在B中&#xff0c;也就是图中的阴影部分&#xff0c;left join 和except方法都可以实现 left join …

Harnessing the Power of LLMs in Practice: A Survey on ChatGPT and Beyond

LLM的系列文章&#xff0c;针对《Harnessing the Power of LLMs in Practice: A Survey on ChatGPT and Beyond》的翻译。 在实践中驾驭LLM的力量——ChatGPT及其后的研究综述 摘要1 引言2 模型实用指南2.1 BERT风格的语言模型&#xff1a;编码器-解码器或仅编码器2.2 GPT风格…

python接口自动化(三十五)-封装与调用--流程类接口关联(详解)

简介 流程相关的接口&#xff0c;主要用 session 关联&#xff0c;如果写成函数&#xff08;如上篇&#xff09;&#xff0c;s 参数每个函数都要带&#xff0c;每个函数多个参数&#xff0c;这时候封装成类会更方便。在这里我们还是以博客园为例&#xff0c;带着小伙伴们实践一…

spring复习:(24)ApplicationContext中的BeanPostProcess是在哪里注册到容器的?

在ApplicationContext实现类的构造方法里。 public ClassPathXmlApplicationContext(String configLocation) throws BeansException {this(new String[] {configLocation}, true, null);}上边的构造方法调用如下构造方法 public ClassPathXmlApplicationContext(String[] conf…

ubuntu使用WHEELTE N100并用rviz显示

写在最开头&#xff0c;如果wheeltec n100被自己改动过参数导致无法读取数据&#xff0c;建议在window的上位机中恢复出厂设置并重新上电&#xff0c;在转入ubuntu。因为我就是这个问题&#xff0c;客服远程操控才帮我解决的。 所有官方资料共享&#xff0c;侵删&#xff1a; …

Flink+StarRocks 实时数据分析新范式

摘要&#xff1a;本文整理自 StarRocks 社区技术布道师谢寅&#xff0c;在 Flink Forward Asia 2022 实时湖仓的分享。本篇内容主要分为五个部分&#xff1a; 极速数据分析 实时数据更新 StarRocks Connector For Apache Flink 客户实践案例 未来规划 点击查看原文视频 &a…

一篇文章让你看懂C语言字符函数和内存函数

目录 一、字符函数 1.strlen函数 1.1strlen函数的介绍 1.2strle函数的使用 1.3模拟实现strlen 1.3.1指针移动法 1.3.2指针减去指针法 1.3.3函数递归法 2.strcpy函数 ​编辑 2.1strcpy函数的介绍 2.2strcpy函数的使用 2.3模拟实现strcpy 3.strcat函数 3.1strcat函数的介…

LiveGBS流媒体平台GB/T28181功能-支持海康大华GB28181语音对讲需要的设备及服务准备

LiveGBS支持海康大华GB28181语音对讲需要的设备及服务准备 1、背景2、准备2.1、服务端必备条件&#xff08;注意&#xff09;2.2、准备语音对讲设备2.2.1、 大华摄像机2.2.1.1、 配置接入示例2.2.1.2、 配置音频通道编号 2.2.2、 海康摄像机2.2.2.1、 配置接入示例 3、开启音频…

初试Python路径库

文章目录 一、pathlib概述二、操作路径对象(一)操作属性(二)连接路径(三)拆分完整路径三、路径对象的常用函数(一)获取当前工作目录(二)创建新目录(三)查看主目录一、pathlib概述 自Python 3.4 以来,pathlib一直是标准库的一部分。 PurePath, PurePosixPath, Pure…

即视角|出海资本热土——印尼市场洞察(上)

即视角Insight 共享即构新洞察&#xff0c;共建行业新动能——ZEGO即构科技基于音视频技术领域的多年深耕&#xff0c;综合面向各行业的服务经验&#xff0c;在【即视角】栏目发布即构对行业的洞察。 此前我们根据即构对出海客户的服务经验&#xff0c;输出了文章《即视角&am…

STL标准模板库 set容器

文章目录 迭代器迭代器的五大分类迭代器系列帮手函数一览 set容器打印任意 STL 容器的printer.hset与vectorset 和 vector 的区别set 和 vector 迭代器的共同点set 和 vector 迭代器的不同点 set 的排序set 的排序&#xff1a;string 会按“字典序”来排set 的排序&#xff1a;…

ai智能绘画生成器有哪些?你知道ai生成图片网站哪个好吗?

曾经有一个年轻的画家&#xff0c;名叫亚历克斯。他对艺术充满了热情和渴望&#xff0c;但却常常感到自己的创作灵感有限。每当他拿起画笔&#xff0c;总是困扰于如何将心中的景象完美地呈现在画布上。 有一天&#xff0c;亚历克斯偶然听说了一个神奇的网站&#xff0c;据说这…

【力扣】20. 有效的括号

有效的括号 给定一个只包括 (&#xff0c;)&#xff0c;{&#xff0c;}&#xff0c;[&#xff0c;] 的字符串 s &#xff0c;判断字符串是否有效。 有效字符串需满足&#xff1a; 左括号必须用相同类型的右括号闭合。 左括号必须以正确的顺序闭合。 每个右括号都有一个对应的相…

uniapp H5预览PDF文件

1&#xff0c;下载资源后hybrid文件存放在static静态文件里 (点击这里去下载文件) 2&#xff0c;pdf预览页面配置 <template><view style"width: 100vh;"><web-view :src"pdfUrl"></web-view></view> </template><…

【每日一题】1289. 下降路径最小和 II

【每日一题】1289. 下降路径最小和 II 1289. 下降路径最小和 II题目描述解题思路 1289. 下降路径最小和 II 题目描述 给你一个 n x n 整数矩阵 grid &#xff0c;请你返回 非零偏移下降路径 数字和的最小值。 非零偏移下降路径 定义为&#xff1a;从 grid 数组中的每一行选择…

【计算机视觉】简述对EQ-Net的理解

最近又看了一些点云分割的文章&#xff0c;近两年点云分割的文章是真的少&#xff0c;不知道是不是点云分割算法接近了末端。这篇文章主要提出了一个基于查询方法的统一范式&#xff0c;它解决了一些不仅仅是点云分割的问题&#xff0c;还解决了三维点云分类和三维目标检测的问…

解密AI图像安全技术:智能守护数智时代,低代码平台助力圈复杂操作!

前言 随着数智时代的来临&#xff0c;人们进入了一个全新的智能化世界。在这个时代中&#xff0c;人工智能&#xff08;AI&#xff09;成为了一项重要的技术突破&#xff0c;其应用也无处不在。其中&#xff0c;AI图像安全技术作为保障个人和企业数据安全的重要环节&#xff0c…

Linux——认识Linux的目录结构 常用命令 vim命令 权限及其控制

目录 linux的目录结构常用linux的命令ls(list)和llcd 切换目录mkdir 创建文件夹touch命令&#xff1a;创建普通文本文件pwd 显示路径whoamisu&#xff1a;普通--超级账号man&#xff1a;查看手册rm&#xff1a;删除网络命令ifconfig重定向 >>cat 查看文本文件clear清屏hi…

3.2 Bootstrap 下拉菜单(Dropdowns)

文章目录 Bootstrap 下拉菜单&#xff08;Dropdowns&#xff09;选项对齐标题 更多实例 Bootstrap 下拉菜单&#xff08;Dropdowns&#xff09; 本章将重点介绍 Bootstrap 下拉菜单。下拉菜单是可切换的&#xff0c;是以列表格式显示链接的上下文菜单。这可以通过与 下拉菜单&a…

NZ12:VBA给批量文件重命名

【分享成果&#xff0c;随喜正能量】沉默&#xff0c;可以让混乱的心&#xff0c;变得清澈。沉默&#xff0c;是城府&#xff0c;是睿智&#xff0c;是内涵&#xff1b;沉默&#xff0c;是最后的清高&#xff0c;也是最后的自由。。 我的教程一共九套及VBA汉英手册一部&#x…