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

linux——线程

在 Linux 系统中,进程和线程是两种重要的并发执行单元。本文将详细介绍它们的区别、使用场景、以及多线程编程中的关键API和示例代码。

进程与线程的区别

进程

  • 进程是程序运行的一个实例,承担分配系统资源的基本单位。
  • 每个进程都有独立的地址空间,一个进程崩溃不会影响其他进程。
  • 进程的创建和切换消耗较多资源。

线程

  • 线程是进程中的一个执行路径,是CPU调度的基本单位。
  • 线程共享进程的地址空间,但每个线程有自己的堆栈和局部变量。
  • 线程的创建和切换开销较小。
  • 如果一个线程崩溃,会导致整个进程崩溃。

使用线程的理由

  1. 节省资源:创建进程需要分配独立的地址空间,建立多个数据表来维护其代码段、数据段和堆栈段,这种方式十分昂贵。线程共享同一进程的地址空间和大部分数据,启动一个线程比启动一个进程要快很多。一个进程的开销大约是一个线程的30倍。
  2. 方便的通信机制:不同进程之间的数据传递需要通过通信机制,如管道、信号等,这种方式耗时且复杂。而线程共享进程的数据空间,数据共享非常方便和快捷,但需要注意数据同步的问题。

多线程开发及API

多线程开发主要包含三点:线程、互斥锁、条件变量。以下是具体的操作和API介绍:

线程操作

线程的创建
#include <pthread.h>
int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void *), void *restrict arg);
// 返回:若成功返回0,否则返回错误编号
线程的获取和比较
#include <pthread.h>
pthread_t pthread_self(void);
线程的等待
#include <pthread.h>
int pthread_join(pthread_t thread, void **rval_ptr);
// 参数:
// pthread_t thread:等待的线程
// void **rval:线程退出状态的收回,NULL表示不收回
线程的退出
#include <pthread.h>
int pthread_exit(void *rval_ptr);

线程的创建、退出、等待示例

#include <stdio.h>
#include <pthread.h>void *func1(void *arg)
{static int ret = 10;printf("t1:%ld thread is created\n", (unsigned long)pthread_self());printf("t1: parameter is %d\n", *((int *)arg));pthread_exit((void*)&ret); // 线程退出
}int main()
{int ret;int param = 100;int *pret = NULL;pthread_t t1;ret = pthread_create(&t1, NULL, func1, (void*)&param); // 创建线程if(ret == 0){printf("main: create t1 success\n");}printf("main: %ld\n", (unsigned long)pthread_self()); // 获取线程IDpthread_join(t1, (void**)&pret); // 等待线程退出printf("main: t1 quit with %d\n", *pret);return 0;
}

传入一个结构体的线程创建示例

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>struct data
{int a;char *s;
};void *func1(void *arg)
{static char *x = "t1 run out";struct data *temp;temp = (struct data*)arg;printf("t1:%ld pthread is created\n", (unsigned long)pthread_self());printf("t1: %d\n", temp->a);printf("t1: %s\n", temp->s);pthread_exit((void*)x);
}int main()
{int ret;pthread_t t1;char *pret = NULL;struct data *p = (struct data*)malloc(sizeof(struct data));p->a = 1;p->s = "xiancheng";ret = pthread_create(&t1, NULL, func1, (void*)p);if(ret == 0){printf("main: create t1 success\n");}printf("main: %ld\n", (unsigned long)pthread_self());pthread_join(t1, (void**)&pret);printf("main: t1 quit with %s\n", pret);free(p);return 0;
}

线程共享空间验证示例

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>int g_data = 10;void *func1(void *arg)
{printf("t1:%ld thread is created\n", (unsigned long)pthread_self());printf("t1: parameter is %d\n", *((int*)arg));while(1){printf("%d\n", g_data++);sleep(1);if(g_data == 3){pthread_exit(NULL);}}
}void *func2(void *arg)
{printf("t2:%ld thread is created\n", (unsigned long)pthread_self());printf("t2: parameter is %d\n", *((int*)arg));while(1){printf("%d\n", g_data++);sleep(1);}
}int main()
{int ret;int param = 100;pthread_t t1;pthread_t t2;ret = pthread_create(&t1, NULL, func1, (void*)&param);if(ret == 0){printf("main: create t1 success\n");}ret = pthread_create(&t2, NULL, func2, (void*)&param);if(ret == 0){printf("main: create t2 success\n");}printf("main: %ld\n", (unsigned long)pthread_self());while(1){printf("%d\n", g_data++);sleep(1);} pthread_join(t1, NULL);pthread_join(t2, NULL);return 0;
}

互斥锁(Mutex)

互斥锁API
创建及销毁互斥锁
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
int pthread_mutex_destroy(pthread_mutex_t *restrict mutex);
加锁及解锁
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *restrict mutex);
int pthread_mutex_trylock(pthread_mutex_t *restrict mutex);
int pthread_mutex_unlock(pthread_mutex_t *restrict mutex);

使用互斥锁的示例

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>int g_data = 0;
pthread_mutex_t mutex; // 定义锁void *func1(void *arg)
{pthread_mutex_lock(&mutex); // 加锁for(int i = 0; i < 5; i++){printf("t1: %ld thread is created\n", (unsigned long)pthread_self());printf("t1: parameter is %d\n", *((int*)arg));sleep(1);}pthread_mutex_unlock(&mutex); // 解锁
}void *func2(void *arg)
{pthread_mutex_lock(&mutex);for(int i = 0; i < 5; i++){printf("t2: %ld thread is created\n", (unsigned long)pthread_self());printf("t2: parameter is %d\n", *((int*)arg));sleep(1);}pthread_mutex_unlock(&mutex);
}void *func3(void *arg)
{pthread_mutex_lock(&mutex);for(int i = 0; i < 5; i++){printf("t3: %ld thread is created\n", (unsigned long)pthread_self());printf("t3: parameter is %d\n", *((int*)arg));sleep(1);}pthread_mutex_unlock(&mutex);
}int main()
{int ret;int param = 100;pthread_t t1;pthread_t t2;pthread_t t3;pthread_mutex_init(&mutex, NULL); // 初始化锁ret = pthread_create(&t1, NULL, func1, (void*)&param);if(ret == 0){printf("main: create t1 success\n");}ret = pthread_create(&t2, NULL, func2, (void*)&param);if(ret == 0){printf("main: create t2 success\n");}ret = pthread_create(&t3, NULL, func3, (void*)&param);if(ret == 0){printf("main: create t3 success\n");}printf("main: %ld\n", (unsigned long)pthread_self());pthread_join(t1, NULL);pthread_join(t2, NULL);pthread_join(t3, NULL);pthread_mutex_destroy(&mutex); // 收回锁return 0;
}

互斥锁限制共享资源的访问示例

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>int g_data = 0;
pthread_mutex_t mutex;void *func1(void *arg)
{printf("t1: %ld pthread is created\n", (unsigned long)pthread_self());printf("t1: parameter is %d\n", *((int*)arg));while(1){pthread_mutex_lock(&mutex);printf("%d\n", g_data++);sleep(1);if(g_data == 3){pthread_mutex_unlock(&mutex);pthread_exit(NULL);}}
}void *func2(void *arg)
{printf("t2: %ld pthread is created\n", (unsigned long)pthread_self());printf("t2: parameter is %d\n", *((int*)arg));while(1){printf("%d\n", g_data);pthread_mutex_lock(&mutex);g_data++;pthread_mutex_unlock(&mutex);sleep(1); }
}int main()
{int ret;int param = 100;pthread_t t1;pthread_t t2;pthread_mutex_init(&mutex, NULL);ret = pthread_create(&t1, NULL, func1, (void*)&param);if(ret == 0){printf("main: create t1 success\n");}ret = pthread_create(&t2, NULL, func2, (void*)&param);if(ret == 0){printf("main: create t2 success\n");}printf("main: %ld\n", (unsigned long)pthread_self());pthread_join(t1, NULL);pthread_join(t2, NULL);pthread_mutex_destroy(&mutex);return 0;
}

死锁示例

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>pthread_mutex_t mutex;
pthread_mutex_t mutex2;void *func1(void *arg)
{pthread_mutex_lock(&mutex);sleep(1);pthread_mutex_lock(&mutex2);for(int i = 0; i < 5; i++){printf("t1: %ld\n", (unsigned long)pthread_self());printf("t1: parameter is %d\n", *((int *)arg));sleep(1);}pthread_mutex_unlock(&mutex);pthread_mutex_unlock(&mutex2);
}void *func2(void *arg)
{pthread_mutex_lock(&mutex2);sleep(1);pthread_mutex_lock(&mutex);for(int i = 0; i < 5; i++){printf("t2: %ld\n", (unsigned long)pthread_self());printf("t2: parameter is %d\n", *((int *)arg));sleep(1);}pthread_mutex_unlock(&mutex2);pthread_mutex_unlock(&mutex);
}void *func3(void *arg)
{pthread_mutex_lock(&mutex);for(int i = 0; i < 5; i++){printf("t3: %ld\n", (unsigned long)pthread_self());printf("t3: parameter is %d\n", *((int *)arg));sleep(1);}pthread_mutex_unlock(&mutex);
}int main()
{int ret;int param = 100;pthread_t t1;pthread_t t2;pthread_t t3;pthread_mutex_init(&mutex, NULL);pthread_mutex_init(&mutex2, NULL);ret = pthread_create(&t1, NULL, func1, (void*)&param);if(ret == 0){printf("main: create t1 success\n");}ret = pthread_create(&t2, NULL, func2, (void*)&param);if(ret == 0){printf("main: create t2 success\n");}ret = pthread_create(&t3, NULL, func3, (void*)&param);if(ret == 0){printf("main: create t3 success\n");}printf("main: %ld\n", (unsigned long)pthread_self());pthread_join(t1, NULL);pthread_join(t2, NULL);pthread_join(t3, NULL);pthread_mutex_destroy(&mutex);pthread_mutex_destroy(&mutex2);return 0;
}

条件变量实现线程同步

条件变量API
创建及销毁条件变量
#include <pthread.h>
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
int pthread_cond_destroy(pthread_cond_t cond);
等待
#include <pthread.h>
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict timeout);
触发
#include <pthread.h>
int pthread_cond_signal(pthread_cond_t *restrict cond);
int pthread_cond_broadcast(pthread_cond_t cond);

使用条件变量的示例

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>int g_data = 0;
pthread_mutex_t mutex;
pthread_cond_t cond;void *func1(void *arg)
{static int cnt = 10;printf("t1: %ld pthread is created\n", (unsigned long)pthread_self());printf("t1: parameter is %d\n", *((int*)arg));while(1){pthread_cond_wait(&cond, &mutex); // 等待if(g_data == 3){printf("t1 run=========================\n");}printf("t1: %d\n", g_data);g_data = 0;sleep(1);if(cnt++ == 10){exit(1);}}
}void *func2(void *arg)
{printf("t2: %ld pthread is created\n", (unsigned long)pthread_self());printf("t2: parameter is %d\n", *((int*)arg));while(1){printf("t2: %d\n", g_data);pthread_mutex_lock(&mutex);printf("%d\n", g_data++);if(g_data == 3){pthread_cond_signal(&cond); // 触发}pthread_mutex_unlock(&mutex);sleep(1);}
}int main()
{int ret;int param = 100;pthread_t t1;pthread_t t2;pthread_cond_init(&cond, NULL);pthread_mutex_init(&mutex, NULL);ret = pthread_create(&t1, NULL, func1, (void *)&param);if(ret == 0){printf("main: create t1 success\n");}ret = pthread_create(&t2, NULL, func2, (void *)&param);if(ret == 0){printf("main: create t2 success\n");}pthread_join(t1, NULL);pthread_join(t2, NULL);pthread_mutex_destroy(&mutex);pthread_cond_destroy(&cond);return 0;
}

通过上述示例和API的讲解,本文详细介绍了Linux下进程与线程的区别、多线程开发的基本操作以及常见问题和解决方案。希望能够帮助大家更好地理解和使用多线程编程。

相关文章:

linux——线程

在 Linux 系统中&#xff0c;进程和线程是两种重要的并发执行单元。本文将详细介绍它们的区别、使用场景、以及多线程编程中的关键API和示例代码。 进程与线程的区别 进程 进程是程序运行的一个实例&#xff0c;承担分配系统资源的基本单位。每个进程都有独立的地址空间&…...

install nebula with source

linux 环境&#xff1a;ubuntu 2004 默认gcc 7.5 nebula requerment&#xff1a; g 8.5 above 下载source git clone --branch release-3.8 https://github.com/vesoft-inc/nebula.git install gcc g 11 apt install gcc-11 g-11 此时 linux环境存在多个版本gcc&#xff1a…...

拆分盘投资策略解析:机制、案例与风险考量

一、引言 随着互联网技术的迅猛发展和金融市场的不断创新&#xff0c;拆分盘这一投资模式逐渐崭露头角&#xff0c;成为投资者关注的焦点。它基于特定的拆分策略&#xff0c;通过调整投资者持有的份额和单价&#xff0c;实现了看似稳健的资产增长。本文旨在深入探讨拆分盘的运…...

Redis主从复制、哨兵模式以及Cluster集群

一.主从复制 1.主从复制的概念 主从复制&#xff0c;是指将一台Redis服务器的数据&#xff0c;复制到其他的Redis服务器。前者称为主节点(Master)&#xff0c;后者称为从节点(Slave)&#xff1b;数据的复制是单向的&#xff0c;只能由主节点到从节点。默认情况下&#xff0c;…...

【chatgpt】npy文件和npz文件区别

npy文件和npz文件都是用于存储NumPy数组的文件格式。它们的主要区别如下&#xff1a; npy文件&#xff1a;这种文件格式用于存储单个NumPy数组。它是一种简单的二进制文件格式&#xff0c;可以快速地读写NumPy数组。 npz文件&#xff1a;这种文件格式是一个压缩包&#xff0c;…...

为什么IP地址会被列入黑名单?

您是否曾经历过网站访客数量骤减或电子邮件投递失败的困扰&#xff1f;这背后或许隐藏着一个常被忽略的原因&#xff1a;您的IP地址可能已经被列入了黑名单内。尽管您并没有进行任何违法的网络操作&#xff0c;但这个问题依然可能出现。那么&#xff0c;究竟黑名单是什么&#…...

【OceanBase诊断调优】—— 如何查找表被哪些其它表引用外键

本文详述如何查找指定表是否被其他表引用做外键。 适用版本 OceanBase 数据库所有版本。 MySQL 租户 obclient> select * from INFORMATION_SCHEMA.KEY_COLUMN_USAGE where REFERENCED_TABLE_NAME表名;Oracle 租户 obclient> SELECT TABLE_NAME FROM dba_constraint…...

网络编程常见问题

1、TCP状态迁移图 2、TCP三次握手过程 2.1、握手流程 1、TCP服务器进程先创建传输控制块TCB&#xff0c;时刻准备接受客户进程的连接请求&#xff0c;此时服务器就进入了LISTEN&#xff08;监听&#xff09;状态&#xff1b; 2、TCP客户进程也是先创建传输控制块TCB&#xff…...

回调函数的使用详解

实际工作中&#xff0c;经常使用回调函数。用来实现触发等机制&#xff0c;也是基于一些已开发好的底层平台&#xff0c;开发上层应用的常用方法。下面对回调函数做一个详细的解释。 目录 1. 简单的回调函数实例 2. C11&#xff0c;使用function<>的写法 3. 注册函数 …...

<电力行业> - 《第8课:输电(一)》

1 输电环节的意义 电能的传输&#xff0c;是电力系统整体功能的重要组成环节。发电厂与电力负荷中心通常都位于不同地区。在水力、煤炭等一次能源资源条件适宜的地点建立发电厂&#xff0c;通过输电可以将电能输送到远离发电厂的负荷中心&#xff0c;使电能的开发和利用超越地…...

【python学习】 __pycache__ 文件是什么

__pycache__文件是Python中的一个特殊目录&#xff0c;主要用于存储已编译的字节码文件&#xff08;.pyc文件&#xff09;。以下是关于__pycache__文件的详细解释&#xff1a; 作用&#xff1a;当Python解释器执行一个模块时&#xff0c;它会首先检查是否存在对应的.pyc文件。…...

论文阅读_基本于文本嵌入的信息提取

英文名&#xff1a;Embedding-based Retrieval with LLM for Effective Agriculture Information Extracting from Unstructured Data 中文名&#xff1a;基于嵌入的检索&#xff0c;LLM 从非结构化数据中提取有效的农业信息 地址: https://arxiv.org/abs/2308.03107 时间&…...

kafka学习笔记08

Springboot项目整合spring-kafka依赖包配置 有这种方式&#xff0c;就是可以是把之前test里的配置在这写上&#xff0c;用Bean注解上。 现在来介绍第二种方式&#xff1a; 1.添加kafka依赖&#xff1a; 2.添加kafka配置方式: 编写代码发送消息&#xff1a; 测试&#xff1a; …...

Flask的 preprocess_request

理解 Flask 类似框架中的 preprocess_request 方法 在 Flask 类似的 web 框架中&#xff0c;preprocess_request 方法是一个关键组件。它在请求被分派之前调用&#xff0c;用于执行一些预处理操作。让我们一步一步来理解这个方法的工作原理。 1. 方法概述 首先&#xff0c;我…...

重温react-05(类组件生命周期和性能优化)

类组件的生命周期 import React, { Component } from reactexport default class learnReact05 extends Component {state {number: 1}render() {return (<div>{this.state.number}</div>)}// 一般将请求的方法,放在这个生命周期componentDidMount() {setInterva…...

RHCE四---web服务器的高级优化方案

一、Web服务器&#xff08;2&#xff09; 基于https协议的静态网站 概念解释 HTTPS&#xff08;全称&#xff1a;Hyper Text Transfer Protocol over Secure Socket Layer 或 Hypertext TransferProtocol Secure&#xff0c;超文本传输安全协议&#xff09;&#xff0c;是以…...

Pytest集成Allure生成测试报告

# 运行并输出报告在Report文件夹下 查看生成的allure报告 1. 生成allure报告&#xff1a;pycharm terminal中输入命令&#xff1a;产生报告文件夹 pytest -s --alluredir../report 2. pycharm terminal中输入命令&#xff1a;查看生成的allure报告 allure serve ../report …...

SpringBoot 参数校验

参数校验 引入springvalidation依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId> </dependency>参数前添加Pattern public Result registry(Pattern(regexp &qu…...

【Arduino】实验使用ESP32控制可编程继电器制作跑马灯(图文)

今天小飞鱼实验使用ESP控制继电器&#xff0c;为了更好的掌握继电器的使用方法这里实验做了一个跑马灯的效果。 这里用到的可编程继电器&#xff0c;起始原理并不复杂&#xff0c;同样需要ESP32控制针脚输出高电平或低电平给到继电器&#xff0c;继电器使用这个信号控制一个电…...

islower()方法——判断字符串是否全由小写字母组成

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 语法参考 islower()方法用于判断字符串是否由小写字母组成。islower()方法的语法格式如下&#xff1a; str.islower() 如果字符串中包含至少一个区…...

React Native 导航系统实战(React Navigation)

导航系统实战&#xff08;React Navigation&#xff09; React Navigation 是 React Native 应用中最常用的导航库之一&#xff0c;它提供了多种导航模式&#xff0c;如堆栈导航&#xff08;Stack Navigator&#xff09;、标签导航&#xff08;Tab Navigator&#xff09;和抽屉…...

pam_env.so模块配置解析

在PAM&#xff08;Pluggable Authentication Modules&#xff09;配置中&#xff0c; /etc/pam.d/su 文件相关配置含义如下&#xff1a; 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块&#xff0c;负责验证用户身份&am…...

pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)

目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关&#xff0…...

C/C++ 中附加包含目录、附加库目录与附加依赖项详解

在 C/C 编程的编译和链接过程中&#xff0c;附加包含目录、附加库目录和附加依赖项是三个至关重要的设置&#xff0c;它们相互配合&#xff0c;确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中&#xff0c;这些概念容易让人混淆&#xff0c;但深入理解它们的作用和联…...

2025年渗透测试面试题总结-腾讯[实习]科恩实验室-安全工程师(题目+回答)

安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 腾讯[实习]科恩实验室-安全工程师 一、网络与协议 1. TCP三次握手 2. SYN扫描原理 3. HTTPS证书机制 二…...

MacOS下Homebrew国内镜像加速指南(2025最新国内镜像加速)

macos brew国内镜像加速方法 brew install 加速formula.jws.json下载慢加速 &#x1f37a; 最新版brew安装慢到怀疑人生&#xff1f;别怕&#xff0c;教你轻松起飞&#xff01; 最近Homebrew更新至最新版&#xff0c;每次执行 brew 命令时都会自动从官方地址 https://formulae.…...

提升移动端网页调试效率:WebDebugX 与常见工具组合实践

在日常移动端开发中&#xff0c;网页调试始终是一个高频但又极具挑战的环节。尤其在面对 iOS 与 Android 的混合技术栈、各种设备差异化行为时&#xff0c;开发者迫切需要一套高效、可靠且跨平台的调试方案。过去&#xff0c;我们或多或少使用过 Chrome DevTools、Remote Debug…...

如何配置一个sql server使得其它用户可以通过excel odbc获取数据

要让其他用户通过 Excel 使用 ODBC 连接到 SQL Server 获取数据&#xff0c;你需要完成以下配置步骤&#xff1a; ✅ 一、在 SQL Server 端配置&#xff08;服务器设置&#xff09; 1. 启用 TCP/IP 协议 打开 “SQL Server 配置管理器”。导航到&#xff1a;SQL Server 网络配…...

yaml读取写入常见错误 (‘cannot represent an object‘, 117)

错误一&#xff1a;yaml.representer.RepresenterError: (‘cannot represent an object’, 117) 出现这个问题一直没找到原因&#xff0c;后面把yaml.safe_dump直接替换成yaml.dump&#xff0c;确实能保存&#xff0c;但出现乱码&#xff1a; 放弃yaml.dump&#xff0c;又切…...

深入理解 React 样式方案

React 的样式方案较多,在应用开发初期,开发者需要根据项目业务具体情况选择对应样式方案。React 样式方案主要有: 1. 内联样式 2. module css 3. css in js 4. tailwind css 这些方案中,均有各自的优势和缺点。 1. 方案优劣势 1. 内联样式: 简单直观,适合动态样式和…...