Linux下多线程编程思考与学习----01(线程创建pthread_create函数详解)

2023-11-06

一、为什么需要使用多线程编程?

        当在执行某些程序的时候难免会需要同时执行两个、甚至多个任务,当然可以使用多个进程进行执行,但是难免需要用到信息的传输,因此就需要引入进程间通信的问题,这对于CPU内存调度的压力也会更大。多线程编程的优点是在同一个进程下,多个线程可以访问访问同一个全局变量,这使得多个线程之间的沟通交互更加便捷,对CPU资源消耗也会越少。(在Linux系统中,调度是以线程为单位的;但是资源的分配是以进程为单位的)

二、如何使用:

        1、如何创建线程?

#include <pthread.h>

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

RETURN VALUE
       On  success,  pthread_create() returns 0; on error, it returns an error number, and the contents of *thread are undefined.

第一个参数用于存放指向pthread_t类型的指针(指向该线程tid号的地址)
第二个参数表示了线程的属性,一般以NULL表示默认属性
第三个参数是一个函数指针,就是线程执行的函数。这个函数返回值为 void*,
形参为 void*
第四个参数则表示为向线程处理函数传入的参数,若不传入,可用 NULL 填充(第四个参数设计到void*变量的强制转化,具体使用后续demo中说明)

返回值:成功,返回0;失败,返回一个错误码。可以使用 perror()函数查看错误原因。

1.1 线程的tid号:

每一个线程都会有一个类似进程的pid号,类似我们的“身份证”,叫做tid号。其实就是一个pthread_t 类型的变量。线程号与进程号是表示线程和进程的唯一标识,但是对于线程号而言,其仅仅在其所属的进程上下文中才有意义。

在pthread库中也有相对应的函数来获取该线程的tid号:

#include <pthread.h>
pthread_t pthread_self(void);
成功:返回线程号

例程:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

void* pthread1Handler()
{
	printf("%ld\n",pthread_self());
	
}

int main()
{

	int back;
	pthread_t th1;

	back = pthread_create(&th1,NULL,pthread1Handler ,NULL);

	if(back != 0){
		printf("create pthread failed\n");
		perror("Why: \n");
		return -1;
	}

	printf("main:%ld\n;th1:%ld\n ",pthread_self(),th1);

	sleep(1);

	return 0;
}

结果:
 

 此输出结果因为涉及到线程之间的资源争夺,main函数仅打印了部分数据,后半部分数据被th1线程争夺输出了结果。当主线程伴随进程结束时,所创建出来的线程也会立即结束,不会继续执行。并且创建出来的线程的执行顺序是随机竞争的,并不能保证哪一个线程会先运行。

        2、为线程传入参数

        pthread_create() 的最后一个参数的为 void*类型的数据,表示可以向线程传递一个 void*数据类型的参数,线程的回调函数中可以获取该参数。该参数可以是任意类型的变量,如普通int型变量、也可以是一个指针,同时也可以是结构体。其中涉及到了void* 参数的强制转换,以及一些细节需要注意!!!

        2、1如何传入一个普通变量(以int型变量为例,同时传递一个指针变量加以区分)

例程:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

void* pthread1Handler(void* arg)
{
	printf("get arg = %d,addr: %p\n",(int)(long)arg,arg);
}

void* pthread2Handler(void* arg)
{
	printf("get arg = %d,addr: %p\n",*(int*)arg,arg);
}

int main()
{

	int ret;
	int a = 100;
	
	pthread_t th1;
	pthread_t th2;

	ret = pthread_create(&th1,NULL,pthread1Handler,(void*)(long)a); //向pthread1传入变量a的值

	if(ret != 0){
		printf("create pthread failed\n");
		perror("Why: \n");
		return -1;
	}

	ret = pthread_create(&th2,NULL,pthread2Handler,(void*)&a); //向pthread2传入变量a的地址
	if(ret != 0){
		printf("create pthread failed\n");
		perror("Why: \n");
		return -1;
	}

	sleep(1);

	printf("This is %d ,addr:%p\n",a,&a);

	return 0;
}

结果:


输出的结果中,因为pthread2传入的局部变量a的地址,所以其打印的结果与main函数中打印出的地址是相同的。pthread1当中传入的是变量的值,因此在pthread1中打印的地址仅仅是pth1回调函数中行参的地址,在线程结束后,该地址就会被释放。

如何传入?(地址、变量值)

地址:使用取地址符号 &,传入该变量的地址。但是在编译的过程当中,因为在该函数要求传入的参数类型为(void*),与传入的类型不符,需要将传入的数据强制转换成 void*。

使用:在回调函数当中,可以将 void* 强制转换成为 int* 变量,因为该指针当中存放的是main函数当中a变量的地址,访问a变量需要取int*当中的值。(*(int*)arg)

变量值(以 int类型 为例):传递int类型的变量值时,只需要将该类型的变量强制装换成void*传入即可((void*)(long)a)。针对不同位数机器,指针对其字数不同,需要 int 转化为 long
在转指针,否则可能会发生警告

使用sizeof(void *) 和 sizeof(int)的输出它们的大小分别为8和4(不同的操作系统不一样)所以编译后才出现int 到 (void *)转换大小不匹配.

使用:在回调函数当中,可以直接将该变量转换成相应的变量类型使用即可。

错误使用:不要传入动态变化的参数。

例程:

void* func(void* arg)
{
    int value = *(int*)arg;
    printf("%d\n",value);
}

int main(void)
{
    pthread_t pid[6];
    int ret;
    for (int i=0; i<6; ++i)
    {   
        if ((ret=pthread_create(&pid[i],NULL,thread,(void*)&i)) != 0)
        {   
            fprintf(stderr,"pthread_create:%s\n",strerror(ret));
            exit(1);
        }   
    } 
}  
void* func(void* arg)
{
    int value = *(int*)arg;
    //free(arg);//------------------->在参数使用结束后,对malloc的内存释放。
    printf("%d\n",value);
    pthread_exit(arg);
}

int main(void)
{
    pthread_t pid[6];
    int ret;
    void *ptr;
    for (int i=0; i<6; ++i)
    {   
        int *p = malloc(sizeof(*p));
        if(p==NULL)
        {
            perror("malloc");
            exit(1);
        }

        *p = i;
        if ((ret=pthread_create(&pid[i],NULL,thread,(void*)p)) != 0)
        {   
            fprintf(stderr,"pthread_create:%s\n",strerror(ret));
            exit(1);
        }   
    } 
 
 
    for(int j=0;j<6;j++)
    {
        pthread_join(pid[i],&ptr);//-----------收到线程传过来的参数。
        free(ptr);//-----------------释放内存
        
    }
    exit(0);
}  

在第一个例程中,输出的结果可能会出现错误,传输的是一个动态变化的内存空间,因为线程的创建是需要时间的,但是循环地址空间的内容变化的时间将会快于这个时间,所以会导致内容出错。例程二当中访问的是动态创建的内存空间,里面的内容是固定的,因此每一次传参访问的过程不会出现问题。

        2.2传递一个结构体

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

struct test
{
	int Id;
	char name[100];
	int sex;
};


void* func1(void* arg)
{
	struct test* str = (struct test*) arg;

	printf("This is %s,Id: %d,sex: %d\n",str->name,str->Id,str->sex);
}

int main()
{
	pthread_t pth1;
	int ret;

	struct test stu1;

	stu1.Id   = 1;
	strcpy(stu1.name,"Zhangzhiyi");
	stu1.sex  = 1;

	ret = pthread_create(&pth1,NULL,func1,(void*)&stu1);

	if(ret != 0){
		printf("Pthreat_create failed\n");
		perror("Why:\n");
		return -1;
	}

	sleep(1);//添加该函数为了能够是子线程先执行,看到输出的结果

	printf("main: This is %s,Id: %d,sex: %d\n",stu1.name,stu1.Id,stu1.sex);

	return 0;
}

结果:

 总结:以上是线程在创建过程当中会出现的一些问题的归纳与总结,后续如有会继续补充。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux下多线程编程思考与学习----01(线程创建pthread_create函数详解) 的相关文章

  • _dl_runtime_resolve -- 共享对象何时加载到内存中?

    我们有一个对性能要求很高的消息处理系统 最近我们注意到第一条消息比后续消息花费的时间要长很多倍 当它通过我们的系统时 会发生大量转换和消息增强 其中大部分是通过外部库完成的 我刚刚描述了这个问题 使用 callgrind 将仅一条消息的 运
  • 从sourceforge下载最新版本

    我正在尝试在 bash 脚本中从 Sourceforge 下载最新版本的graphicsmagick wget q https sourceforge net projects graphicsmagick files latest dow
  • 如何在树莓派上更新到最新的 python 3.5.1 版本?

    我昨天拿到了 Raspberry Pi 我已经在尝试用它来编写代码了 我有一个计划在其上运行的程序 但它仅与 Python 版本 3 5 0 或 3 5 1 兼容 并且我在互联网上找到的所有内容似乎都已经过时 与 Python 2 有关 或
  • C/C++ with GCC:静态地将资源文件添加到可执行文件/库

    有人知道如何使用 GCC 将任何资源文件静态编译为可执行文件或共享库文件吗 例如 我想添加永远不会改变的图像文件 如果它们改变了 我无论如何都必须替换该文件 并且不希望它们位于文件系统中 如果这是可能的 我认为这是因为 Visual C f
  • BlueZ D-Bus C,应用 BLE

    我正在尝试编写一个应用程序来搜索附近的蓝牙设备并与它们通信 我的应用程序将用 C 语言编写 并打算在 Linux 下工作 是否有通过 C 中的 D Bus 使用 BlueZ 的教程或示例 此应用程序的目的是从 BLE 中的文件发送数据 你能
  • 无法使用 tar -cvpzf 解压完整目录

    把我的头敲在这上面 I used tar cvpzf file tar gz压缩一个完整的目录 我将文件移动到另一台服务器 并尝试解压缩复制存档的目录 无法使其发挥作用 bash 3 2 tar xvpzf news tar gz tar
  • 警告构建使用导出符号的内核模块

    我有两个内核模块 例如 modA 和 modB modA 导出一个符号EXPORT SYMBOL symA modB 使用它 我有标题modA h对于modA extern void symA int param and in modB c
  • 如何从powershell获取主机名?

    我如何获得hostname https stackoverflow com q 42014215 262852 for dur来自 powershell PS home thufir powershell gt PS home thufir
  • 我如何知道 std::map 插入成功还是失败?

    我在多线程应用程序中有一个映射 将名为 uuid 的类映射到指针 我想知道插入操作是否成功或失败 e g mymap insert hint MyMap value type entry uuid itemptr 如果失败的话会抛出异常或者
  • 使用 xargs 时如何获取退出代码(并行)

    我制作了一个用于启动并行 rsync 进程的脚本 bin bash LIST 1 DEST DIR 2 RSYNC OPTS 3 echo rsyncing From SRC DIR To DEST DIR RSYNC OPTS RSYNC
  • 我可以告诉 Linux 不要交换特定进程的内存吗?

    有没有办法告诉 Linux 它不应该将特定进程的内存交换到磁盘 它是一个 Java 应用程序 所以理想情况下我希望有一种方法可以从命令行执行此操作 我知道您可以将全局交换性设置为 0 但这明智吗 您可以通过以下方式执行此操作姆洛克尔 2 h
  • 多线程进程的线程ID可以与另一个正在运行的进程的进程ID相同吗?

    我正在尝试找到一种方法来唯一标识多进程环境中的线程 我有一个服务器 它跟踪连接到它的不同进程 其中一些是多线程的 一些不是 为了识别多线程连接中的线程 我使用线程 ID 作为唯一标识符 在任何给定时间最多有 1 个多线程进程连接 我的问题是
  • 导出多个 LD_LIBRARY_PATH 的正确方法

    对于linux的使用 我是一个新手 根据有关我的项目的指南 我必须多次导出 LD LIBRARY PATH 并且我不确定是否不覆盖它们 Cupti Tensorflow CUDAit export LD LIBRARY PATH LD LI
  • 容器上的“container_memory_working_set_bytes”指标和 OOM-killer 之间有什么关系?

    我试图找出并理解 OOM killer 如何在容器上工作 为了弄清楚这一点 我读了很多文章 发现 OOM killer 会根据oom score And oom score是由oom score adj以及该进程的内存使用情况 有两个指标c
  • 如何在 .zip 文件中使用 grep

    有 3 个文件 a csv b csv c csv 压缩为 abh zip 现在可以在 abh zip 上执行 grep 命令 是否有任何通配符 仅对里面的 c csv 文件运行 grep压缩 如果你有zipgrep 据我所知 它是随zip
  • 使用多个 NIC 广播 UDP 数据包

    我正在 Linux 中为相机控制器构建嵌入式系统 非实时 我在让网络做我想做的事情时遇到问题 该系统有 3 个 NIC 1 个 100base T 和 2 个千兆端口 我将较慢的连接到相机 这就是它支持的全部 而较快的连接是与其他机器的点对
  • 超立方体错误。非法的最小或最大规格

    尝试从这里运行示例代码http tess4j sourceforge net codesample html http tess4j sourceforge net codesample html我收到一条错误消息 Error Illega
  • C:运行系统命令并获取输出? [复制]

    这个问题在这里已经有答案了 我想在 Linux 中运行一个命令并获取其输出内容的返回文本 但我do not想要将此文本打印到屏幕上 有没有比制作临时文件更优雅的方法 你想要 popen http linux die net man 3 po
  • 如何使用 sed 交换两行?

    有谁知道如何更换line a with line b and line b with line a使用 sed 编辑器在文本文件中 我可以看到如何用保留空间中的一行替换模式空间中的一行 即 Paco x or Paco g 但是如果我想采取
  • 在linux中将包含word的行从一个文件复制到另一个文件

    我想复制包含某些单词的行file1 to file2 Suppose file1 ram 100 ct 50 gopal 200 bc 40 ravi 50 ct 40 krishna 200 ct 100 file2应该只有包含 ct 的

随机推荐

  • 练习二、用JS语言输入数组并判断是否数组,并从小到大进行冒泡排序

    功能描述 在界面输入以逗号间隔开的字符串 转换成数组并判断是否有效 有效即进行排序 选择冒泡排序 主要考点 以输入框为输入参数入口 进行逐步推导 2 1 输入框是字符串需要转换成数组 2 2 一个输入框 要是输入双位数还是会分割成 单位数值
  • Linux中在线yum源的配置

    在线源的配置 配置环境 root localhost cat etc redhat release CentOS release 6 5 Final root localhost uname r 2 6 32 431 el6 x86 64
  • c语言六子棋(Alpha-Beta剪枝算法)

    c语言Alpha Beta剪枝算法六子棋 介绍 Alpha Beta剪枝算法是一种用于优化博弈树搜索的算法 可以在搜索过程中减少不必要的计算 从而提高搜索效率 该算法常用于博弈游戏 如六子棋 六子棋是一种类似于五子棋的棋类游戏 在一个六边形
  • 响应支付宝宣布的刷脸支付商业化

    二维码普及才没多久 刷脸支付已经悄无声息的走进我们的生活 刷脸支付作为一种全新的支付方式 将彻底改变人类的生活方式 有些人感慨道 这个时代变化太快了 快到一切技术的发展犹如坐上了火箭 去年8月中下旬 广电运通与深圳地铁腾讯共同打造生物识别
  • 为什么选择SpringCloud

    SpringCloud的诞生 SpringCloud是集成了众多开源的框架 利用SpringBoot的开发便利性实现服务治理 服务注册与发现 负载均衡 数据监控 REST API发布方式等 基本囊括了分布式框架所需要的所有功能 是一套易开放
  • 饮冰l于 2021-02-05 15:16:54 发布 1288收藏 11分类专栏: 图 文章标签: 算法 数据挖掘 机器学习 深度学习版权图 专栏收录该内容66 篇文章 23 订阅订

    前言 本文的作者认为对于 GNN 的可解释性不足 基于此 作者在节点分类任务上提出可以通过将忽略图结构的浅层模型与两个利用标签结构中相关性的后处理方法相结合 超越或匹配最先进的 GNN 具体如下 i 误差相关性 传播训练数据中的残余误差以纠
  • CMake编程实践(五) 编译静态库和动态库

    使用Cmake 编译库 本篇使用CMake编译一个动态库和静态库 并安装到系统中 对应的工程是cmake utilsbox lib 编译静态库 指定编译静态库 关键词为static 不添加关键字默认静态库 add library utils
  • 存档用【20考研】国家线/34所自划线 集合!【计算机和软件专业】

    为了同学们更方便快捷的了解34所院校的自划线和国家分数线 小编在这里开了一个专区 提供给同学们计算机 软件工程等相关专业的考研分数线 这是20考研的数据 我发现当时没有发 现在发一下存档用的 不发没办法设置链接 什么是A类 什么是B类 可以
  • 【Windows】如何使用Windbg检测Windows程序内存泄漏

    文章目录 使用Windbg检测Windows程序内存泄漏的步骤如下 启动程序 使用Windbg启动需要检测内存泄漏的程序 可以通过在Windbg中选择 文件 gt 打开程序 来打开程序 设置符号路径 在Windbg中 可以通过设置符号路径来
  • python 多线程模拟多用户访问接口

    coding utf 8 import threading import requests from urllib parse import quote 定义访问接口的函数 def access api user id params E4
  • Kafka数据丢失原因及解决方案

    Kafka包括Producer Broker Consumer 因此从这三个方面分析 Producer端 丢失原因 Kafka在Producer端的消息发送采用的是异步发送的方式 还有同步发送 但是同步发送会导致消息阻塞 需要等待 丢失数据
  • XSS漏洞学习

    原理 XSS攻击通常指的是通过利用网页开发时留下的漏洞 通过巧妙的方法注入恶意指令代码到网页 使用户加载并执行攻击者恶意制造的网页程序 这些恶意网页程序通常是JavaScript 但实际上也可以包括Java VBScript ActiveX
  • openmpi参数_Open MPI的目标、概念及实现

    目前 许多可用的MPI实现只关注HPC 高性能计算 的部分方面或是专注于解决某个具体问题 这导致了这些MPI实现不兼容 如果独立安装组合来获取它们的特有功能 Open MPI是在LAM MPI LA MPI and FT MPI的基础上的一
  • 目标检测如何计算召回率_目标检测+准确率、召回率、PR曲线、AP、mAp、mmAp

    查准率 查全率 AP map 参考知乎地址 https zhuanlan zhihu com p 94597205 TP True Positive 一个正确的检测 检测的IOU threshold 即预测的边界框 bounding box
  • JavaScript中常用的三种弹窗

    目录 一 alert 警告框 二 confirm 确认框 三 prompt 提示框 JavaScript 中可以创建三种消息框 警告框 确认框 提示框 一 alert 警告框 alert 方法是显示一条弹出提示消息和确认按钮的警告框 需要注
  • 【js】删除数组中的指定元素,且不改变原数组

    笨办法 export function remove arr item let result arr forEach function element if element item result push element return r
  • 样本不平衡问题分析与部分解决办法

    最近工作中在处理文本分类问题遇到了分类不均衡的问题 主要还是样本太少还同时非常的不均衡正负样本1 10 类别不平衡比例超过4 1 就会造成偏移 就使用了SMOTE方法 注意 在进行数据增广的时候一定要将测试集和验证集单独提前分开 扩张只在训
  • Doxygen简介及使用说明

    https blog csdn net Candy1232009 article details 80786179
  • java的String字符串拼接和StringBuffer的append的速度差异

    今天在leetcode上做题的时候发现 实现字符串拼接的时候 用StringBuffer的append比String的 快很多 思考了一下这两者的差别 我是初学者 有说错的还请大家指正 首先得出String的 拼接的时间 public st
  • Linux下多线程编程思考与学习----01(线程创建pthread_create函数详解)

    一 为什么需要使用多线程编程 当在执行某些程序的时候难免会需要同时执行两个 甚至多个任务 当然可以使用多个进程进行执行 但是难免需要用到信息的传输 因此就需要引入进程间通信的问题 这对于CPU内存调度的压力也会更大 多线程编程的优点是在同一