Linux C语言线程编程入门笔记

目录

  • 开发环境准备

  • 线程基础概念

    • 进程与线程的关系

    • 线程生命周期

  • 创建线程

  • 等待线程结束

  • 线程函数和参数

  • 互斥锁与共享资源保护

  • 总结

开发环境准备

  • 操作系统:以 Linux 为例(Ubuntu/CentOS 等主流发行版)。请确保系统已安装 GNU C 编译器(gcc)。

  • 线程库:POSIX 线程库一般已包含在标准库中。若未安装,可以通过包管理器安装对应开发包。

  • 编译命令:编写完成代码后,可用 gcc 编译,并加上线程选项。示例:

gcc -o myprog main.c -pthread

其中 -pthread(或 -lpthread)选项用于链接 pthread 库​blog.csdn.net。例如出现 undefined reference to pthread_create 错误时,需要添加此选项。

线程基础概念

进程与线程的关系

多个线程示意图展示了同一进程中各线程共享的资源,如代码段、数据段和打开的文件等​cnblogs.com。线程属于进程,一个进程可以包含一个或多个线程​cnblogs.com。一般来说,进程是操作系统进行资源分配和调度的最小单位,而线程是程序执行的最小单位​cnblogs.com。同一进程中的多个线程共享进程的内存空间(包括代码、数据、堆等),但各自拥有独立的寄存器和栈空间​cnblogs.com。多个线程并发执行时,可以提高程序并行度,但也需要注意同步和互斥。

线程生命周期

如上图所示,线程在运行过程中会经历不同状态。通常一个线程的生命周期包括 新建 (New)就绪 (Runnable)运行 (Running)阻塞/等待 (Blocked/Waiting)终止 (Dead) 等阶段​cnblogs.com。当调用 pthread_create() 后,线程从“新建”进入“就绪”状态;线程获得 CPU 时间后进入“运行”状态;如果线程调用 sleep()pthread_join() 等阻塞操作,则进入“阻塞”状态。线程执行完毕或调用 pthread_exit() 后进入“终止”状态​cnblogs.com。掌握这些状态转换有助于理解线程的调度行为和并发执行过程。

创建线程

要创建线程,使用 POSIX 线程库提供的 pthread_create() 函数。其原型如下:

int pthread_create(pthread_t *thread,const pthread_attr_t *attr,void *(*start_routine)(void *),void *arg);
  • thread:指向 pthread_t 类型变量的指针,用于保存新创建线程的 ID。

  • attr:线程属性,一般使用默认值 NULL

  • start_routine:线程入口函数地址(函数指针)。

  • arg:传递给线程函数的参数,类型为 void*

示例:创建一个线程执行简单的打印函数。

#include <stdio.h>
#include <pthread.h>void* say_hello(void* arg) {printf("Hello from thread!\n");return NULL;
}int main() {pthread_t tid;// 创建线程,执行 say_hello 函数pthread_create(&tid, NULL, say_hello, NULL);// 等待线程结束pthread_join(tid, NULL);return 0;
}

以上代码中,pthread_create(&tid, NULL, say_hello, NULL); 会创建一个新线程,该线程运行 say_hello 函数。主线程pthread_create 之后继续往下执行(此例中主线程随后调用了 pthread_join 等待子线程结束)。

等待线程结束

创建线程后,主线程和子线程是并发执行的。有时需要主线程等待子线程完成后再继续,比如输出结果或回收资源。此时使用 pthread_join() 函数。其原型为:

int pthread_join(pthread_t thread, void **retval);
  • 第一个参数为要等待的线程 ID;

  • 第二个参数用于获取线程函数的返回值(可为 NULL 表示不关心返回值)。

上例中 pthread_join(tid, NULL); 会阻塞主线程,直到 tid 对应的子线程执行结束并回收其资源为止。若省略 pthread_join,主线程可能在子线程完成前就退出,导致子线程被强制终止或无法正常输出结果。

线程函数和参数

线程入口函数必须符合 void* func(void* arg) 的形式。函数内参数类型为 void*,可以传递任意指针数据。在线程内部,需要根据实际类型将参数指针转换回来。例如:

#include <stdio.h>
#include <pthread.h>void* print_num(void* arg) {int *p = (int*)arg;      // 转换回 int* 类型printf("num = %d\n", *p);return NULL;
}int main() {pthread_t tid;int value = 42;// 将 &value 作为参数传入线程pthread_create(&tid, NULL, print_num, &value);pthread_join(tid, NULL);return 0;
}

上例中,主线程定义了一个整数 value = 42,并将它的地址传给子线程。子线程在 print_num 函数中把 void* 参数转换为 int* 后,通过 *p 访问该值并打印。注意:被传递的数据(如 value)在子线程访问期间必须有效,如果是局部变量则不能在其作用域结束后再访问。

互斥锁与共享资源保护

多个线程同时访问共享资源(如全局变量或共享数据结构)时,容易发生竞态条件。互斥锁 (mutex) 可以用来保护关键代码区域,确保同一时间只有一个线程访问共享资源。使用方法如下:

  1. 定义一个全局互斥锁变量 pthread_mutex_t lock; 并初始化:

    pthread_mutex_destroy(&lock);
    
  2. 在访问共享资源前调用 pthread_mutex_lock(&lock); 加锁,在访问结束后调用 pthread_mutex_unlock(&lock); 解锁。

  3. 程序结束时释放锁:

pthread_mutex_destroy(&lock);

示例:两个线程同时对全局变量 counter 进行递增操作,使用互斥锁保证结果正确:

#include <stdio.h>
#include <pthread.h>int counter = 0;             // 共享资源
pthread_mutex_t lock;        // 互斥锁void* add(void* arg) {for(int i = 0; i < 100000; i++) {pthread_mutex_lock(&lock);   // 加锁counter++;pthread_mutex_unlock(&lock); // 解锁}return NULL;
}int main() {pthread_t t1, t2;pthread_mutex_init(&lock, NULL); // 初始化互斥锁pthread_create(&t1, NULL, add, NULL);pthread_create(&t2, NULL, add, NULL);pthread_join(t1, NULL);pthread_join(t2, NULL);printf("counter = %d\n", counter);pthread_mutex_destroy(&lock);    // 销毁互斥锁return 0;
}

在上例中,若不使用锁,则两个线程可能会同时读写 counter 导致丢失更新。通过加锁,每次只有一个线程进入临界区 counter++,最终输出的 counter 值才是预期的 200000

总结

本文介绍了在 Linux 下使用 C 语言进行多线程编程的入门知识。从环境准备、编译选项,到线程基本概念(进程与线程的区别、线程生命周期)、以及线程的创建、等待和参数传递方法,都做了简单说明,并给出了最基本的代码示例。最后还演示了使用 互斥锁 来保护共享资源的示例。整体思路清晰、示例简洁,适合 C 语言初学者阅读。学习多线程编程时,要特别关注线程安全和并发问题,并熟练掌握 POSIX 线程库的常用函数。

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

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

相关文章

levelDB的数据查看(非常详细)

起因:.net大作业天气预报程序(WPF)答辩时&#xff0c;老师问怎么维持数据持久性的&#xff0c;启动时加载的数据存在哪里&#xff0c;我明白老师想考的应该是json文件的解析&#xff08;正反&#xff09;&#xff0c;半天没答上来存那个文件了&#xff08;老师默认这个文件是自…

数据分析怎么做?高效的数据分析方法有哪些?

目录 一、数据分析的对象和目的 &#xff08;一&#xff09;数据分析的常见对象 &#xff08;二&#xff09;数据分析的目的 二、数据分析怎么做&#xff1f; &#xff08;一&#xff09;明确问题 &#xff08;二&#xff09;收集数据 &#xff08;三&#xff09;清洗和…

手写 Vue 源码 === 完善依赖追踪与触发更新

目录 依赖收集的完整实现 trackEffects:建立双向依赖关系 触发更新的完整实现 完整的响应式流程 为什么使用 Map 而不是 Set? 总结 在上一篇文章中,我们介绍了 Vue3 响应式系统的基本原理和 activeEffect 的作用。现在,我们将深入探讨完善后的依赖追踪和触发更新机制…

从代码学习深度学习 - 区域卷积神经网络(R-CNN)系列 PyTorch版

文章目录 前言R-CNNFast R-CNN兴趣区域汇聚层 (RoI Pooling)代码示例:兴趣区域汇聚层 (RoI Pooling) 的计算方法Faster R-CNNMask R-CNN双线性插值 (Bilinear Interpolation) 与兴趣区域对齐 (RoI Align)兴趣区域对齐层的输入输出全卷积网络 (FCN) 的作用掩码输出形状总结前言…

18个国内wordpress主题推荐

工厂wordpress中文主题 红蓝色搭配的工厂wordpress中文主题&#xff0c;适合从事生产、加工的工厂官方网站使用。 https://www.jianzhanpress.com/?p8533 Pithy设计师wordpress网站模板 精练简洁的wordpress模板&#xff0c;设计师或设计工作室展示型网站模板。 https://w…

低成本自动化改造技术锚点深度解析

执行摘要 本文旨在深入剖析四项关键的低成本自动化技术&#xff0c;这些技术为工业转型提供了显著的运营和经济效益。文章将提供实用且深入的指导&#xff0c;涵盖老旧设备联网、AGV车队优化、空压机系统智能能耗管控以及此类项目投资回报率&#xff08;ROI&#xff09;的严谨…

Oracle — 数据管理

介绍 Oracle数据库作为全球领先的关系型数据库管理系统&#xff0c;其数据管理能力以高效性、安全性和智能化为核心。系统通过多维度技术实现海量数据的存储与实时处理&#xff0c;支持高并发事务操作与复杂分析查询&#xff0c;满足企业关键业务需求。在安全领域&#xff0c;O…

【PhysUnits】3.3 SI 基础量纲单位(units/base.rs)

一、源码 这段代码定义了一系列基础物理量纲的类型别名&#xff0c;并使用标记 trait Canonical 来表示它们是国际单位制&#xff08;SI&#xff09;中的基本单位。 use crate::Dimension; use typenum::{P1, Z0};/// 标记特质&#xff0c;表示基础量纲单位 pub trait Canoni…

硬件实操技巧记录

本篇自用&#xff0c;防止自己忘记 焊接技巧 一般都是随机电烙铁锡膏组合。 拆电阻时&#xff0c;电烙铁放在电阻上&#xff0c;加锡膏&#xff0c;这个时候熔点会降低&#xff0c;电阻更容易掉下来&#xff0c;用电烙铁带走&#xff1b;焊电阻时&#xff0c;一端点锡膏&…

13.thinkphp的Session和cookie

一&#xff0e;Session 1. 在使用Session之前&#xff0c;需要开启初始化&#xff0c;在中间件文件middleware.php&#xff1b; // Session 初始化 \think\middleware\SessionInit::class 2. TP6.0不支持原生$_SESSION的获取方式&#xff0c;也不支持session_开头的函数&…

TensorFlow中数据集的创建

目录 前言示例示例1示例2示例3示例4 前言 TensorFlow 的 tf.data.Dataset API 提供了一种灵活且高效的方式来加载和预处理数据。它可以轻松处理大规模数据集&#xff0c;并支持多种数据源格式。 所有数据集相关的内容都在tf.data中&#xff0c;from_tensor_slices&#xff1a;…

第十六章,网络型攻击防范技术

网络攻击介绍 网络攻击 --- 指的是入侵或破坏网络上的服务器 ( 主机 ) &#xff0c;盗取服务器的敏感数据或占用网络带宽。 网络攻击分类&#xff1a; 流量型攻击 网络层攻击 应用层攻击 单包攻击 畸形报文攻击 --- 向目标主机发送有缺陷的IP报文&#xff0c;使得目标在…

服务器不备案有影响吗

在当今数字化的时代&#xff0c;服务器成为了众多企业和个人开展业务、展示自我的重要工具。然而&#xff0c;有一个问题常常被忽视&#xff0c;那就是服务器不备案到底有没有影响&#xff1f; 答案是肯定的&#xff01;服务器不备案&#xff0c;影响可不小。据相关数据显示&a…

【LeetCode Solutions】LeetCode 176 ~ 180 题解

CONTENTS LeetCode 176. 第二高的薪水&#xff08;SQL 中等&#xff09;LeetCode 177. 第 N 高的薪水&#xff08;SQL 中等&#xff09;LeetCode 178. 分数排名&#xff08;SQL 中等&#xff09;LeetCode 179. 最大数&#xff08;中等&#xff09;LeetCode 180. 连续出现的数字…

D720201 PCIE 转USB HUB

1. 启动时出现了下面错误 [ 4.682595] pcieport 0004:00:00.0: Signaling PME through PCIe PME interrupt [ 4.684939] pci 0004:01:00.0: Signaling PME through PCIe PME interrupt [ 4.691287] pci 0004:01:00.0: enabling device (0000 -> 0002) [ 5.2962…

【愚公系列】《Manus极简入门》028-创业规划顾问:“创业导航仪”

&#x1f31f;【技术大咖愚公搬代码&#xff1a;全栈专家的成长之路&#xff0c;你关注的宝藏博主在这里&#xff01;】&#x1f31f; &#x1f4e3;开发者圈持续输出高质量干货的"愚公精神"践行者——全网百万开发者都在追更的顶级技术博主&#xff01; &#x1f…

IBM BAW(原BPM升级版)使用教程第六讲

续前篇&#xff01; 一、事件&#xff1a;Undercover Agent 在 IBM Business Automation Workflow (BAW) 中&#xff0c;Undercover Agent (UCA) 是一个非常独特和强大的概念&#xff0c;旨在实现跨流程或系统的事件处理和触发机制。Undercover Agent 主要用于 事件驱动的流程…

【强化学习】动态规划(Dynamic Programming, DP)算法

1、动态规划算法解题 LeetCode 931. 下降路径最小和 给你一个 n x n 的 方形 整数数组 matrix &#xff0c;请你找出并返回通过 matrix 的下降路径 的 最小和 。 下降路径 可以从第一行中的任何元素开始&#xff0c;并从每一行中选择一个元素。在下一行选择的元素和当前行所选…

深入探索DSPy:开启模块化AI编程的新篇章

在当今快速发展的AI时代&#xff0c;语言模型&#xff08;LM&#xff09;的应用已经渗透到各个领域&#xff0c;从简单的文本生成到复杂的多模态任务&#xff0c;语言模型展现出了强大的能力。然而&#xff0c;随着应用场景的日益复杂&#xff0c;开发者们面临着一个共同的挑战…

List<T>中每次取固定长度的数据

工具类方法 package org.common.util; import java.util.ArrayList; import java.util.Iterator; import java.util.List;/*** 批处理取值组件* param <T>*/ public class BatchIterator<T> implements Iterator<List<T>> {private final List<T&g…