【C语言】进程和线程详解

news2024/11/13 8:00:06

LuckiBit

目录

  • C语言进程和线程详解
    • 1. 进程和线程的对比
    • 2. 进程的基本概念
      • 2.1 进程的定义
      • 2.2 进程的特点
      • 2.3 进程的生命周期
    • 3. 进程管理
      • 3.1 进程创建
      • 3.2 进程间通信(IPC)
        • 3.2.1 管道(Pipe)
    • 4. 线程的基本概念
      • 4.1 线程的定义
      • 4.2 线程的特点
    • 5. POSIX线程库
      • 5.1 引用头文件
      • 5.2 创建线程
        • 示例:
      • 创建线程步骤表格
      • 5.3 等待线程结束
      • 5.4 线程同步
        • 5.4.1 互斥锁
        • 示例:
        • 5.4.2 条件变量
        • 示例:
    • 6. 实战:生产者-消费者问题
      • 6.1 问题描述
      • 6.2 解决方案
    • 7. 进程和线程在应用中的选择
    • 8. 总结
    • 9. 结束语
    • 相关文章:

C语言进程和线程详解

1. 进程和线程的对比

在现代操作系统中,进程和线程是实现并发执行的两种主要方式。理解它们的区别和各自的应用场景对于编写高效的并发程序至关重要。

特性进程线程
定义进程是操作系统中独立运行的基本单位,有自己的地址空间和资源。线程是进程中的一个执行单元,多个线程共享同一个进程的资源。
地址空间每个进程有独立的地址空间。线程共享进程的地址空间。
资源开销进程切换开销较大,需保存和恢复全部上下文。线程切换开销较小,只需保存和恢复部分上下文。
通信方式进程间通信(IPC)机制,如管道、消息队列、共享内存等。线程间可以直接通信,共享全局变量和内存。
创建和销毁创建和销毁进程开销较大。创建和销毁线程开销较小。
适用场景适用于需要高隔离性和安全性的独立任务。适用于需要高并发和低开销的任务。

2. 进程的基本概念

2.1 进程的定义

进程是操作系统中独立运行的基本单位,一个进程通常由程序代码、数据段、堆、栈和相关资源(如文件描述符等)组成。

2.2 进程的特点

  • 独立性:每个进程有独立的地址空间。
  • 隔离性:进程之间的数据是隔离的,通常需要通过进程间通信(IPC)进行数据交换。
  • 资源拥有:进程拥有自己的资源,如内存、文件描述符等。

2.3 进程的生命周期

进程的生命周期包括创建、执行、阻塞、唤醒和终止等状态转换。

3. 进程管理

3.1 进程创建

在C语言中,可以使用fork系统调用来创建一个新进程。fork会创建一个与原进程(父进程)几乎相同的新进程(子进程),子进程会从fork调用的地方开始执行。

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

int main() {
    pid_t pid;

    pid = fork(); // 创建子进程

    if (pid < 0) { // 创建失败
        fprintf(stderr, "Fork failed\n");
        return 1;
    } else if (pid == 0) { // 子进程
        printf("This is the child process\n");
    } else { // 父进程
        printf("This is the parent process\n");
    }

    return 0;
}

3.2 进程间通信(IPC)

进程间通信是指在不同进程之间传递数据和信号的机制。常见的IPC方式包括管道、消息队列和共享内存等。

3.2.1 管道(Pipe)

管道是一种单向的通信机制,一个进程可以通过管道将数据发送给另一个进程。

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

int main() {
    int fd[2]; // 文件描述符数组
    char buffer[30];
    pipe(fd); // 创建管道

    if (fork() == 0) { // 子进程
        close(fd[0]); // 关闭读取端
        write(fd[1], "Hello, parent!", 15); // 写入数据
        close(fd[1]); // 关闭写入端
    } else { // 父进程
        close(fd[1]); // 关闭写入端
        read(fd[0], buffer, sizeof(buffer)); // 读取数据
        printf("Received: %s\n", buffer);
        close(fd[0]); // 关闭读取端
    }

    return 0;
}

4. 线程的基本概念

4.1 线程的定义

线程是进程中的一个执行单元,多个线程共享同一个进程的地址空间和资源。线程是实现并发执行的基本单位。

4.2 线程的特点

  • 并发执行:线程可以并发执行,提高程序的响应性和处理能力。
  • 共享资源:线程共享进程的内存和资源,通信和数据共享更方便。
  • 轻量级:线程的创建和切换开销较小。

5. POSIX线程库

POSIX线程库(pthreads)是一个广泛使用的跨平台线程库,适用于Unix和类Unix系统,如Linux和MacOS。通过pthreads库,C语言可以方便地进行多线程编程。

5.1 引用头文件

使用pthreads库时,需要包含pthread.h头文件。

#include <pthread.h>

5.2 创建线程

创建线程可以使用pthread_create函数,该函数原型如下:

int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg);
  • thread:指向线程标识符的指针。
  • attr:线程属性,通常设置为NULL
  • start_routine:线程执行的函数。
  • arg:传递给线程函数的参数。
示例:
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>

// 线程函数,打印传递的消息
void* print_message(void* arg) {
    char* message = (char*)arg;
    printf("%s\n", message);
    return NULL;
}

int main() {
    pthread_t thread; // 线程标识符
    const char* message = "Hello, pthread!"; // 线程参数
    
    // 创建线程
    if (pthread_create(&thread, NULL, print_message, (void*)message)) {
        fprintf(stderr, "Error creating thread\n");
        return 1;
    }
    
    // 等待线程结束
    pthread_join(thread, NULL);
    return 0;
}

创建线程步骤表格

步骤说明代码示例
1包含头文件#include <pthread.h>
2定义线程函数void* print_message(void* arg) { ... }
3声明线程标识符pthread_t thread;
4创建线程并指定线程函数和参数pthread_create(&thread, NULL, ...);
5等待线程结束pthread_join(thread, NULL);

5.3 等待线程结束

使用pthread_join函数可以等待线程结束,原型如下:

int pthread_join(pthread_t thread, void **retval);
  • thread:线程标识符。
  • retval:指向线程返回值的指针。

5.4 线程同步

线程同步是多线程编程中的一个重要问题,pthreads库提供了多种同步机制,如互斥锁(mutex)、条件变量(condition variable)和读写锁(read-write lock)。

5.4.1 互斥锁

互斥锁用于保护共享资源,防止多个线程同时访问,导致数据不一致。

  • 初始化互斥锁
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
  • 锁定互斥锁
pthread_mutex_lock(&lock);
  • 解锁互斥锁
pthread_mutex_unlock(&lock);
示例:
#include <pthread.h>
#include <stdio.h>

pthread_mutex_t lock; // 互斥锁
int counter = 0; // 共享资源

// 线程函数,增加计数器并打印
void* increment_counter(void* arg) {
    pthread_mutex_lock(&lock); // 锁定互斥锁
    counter++;
    printf("Counter: %d\n", counter);
    pthread_mutex_unlock(&lock); // 解锁互斥锁
    return NULL;
}

int main() {
    pthread_t thread1, thread2;
    
    pthread_mutex_init(&lock, NULL); // 初始化互斥锁
    
    pthread_create(&thread1, NULL, increment_counter, NULL); // 创建线程1
    pthread_create(&thread2, NULL, increment_counter, NULL); // 创建线程2

    // 等待两个线程结束
    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);
    
    pthread_mutex_destroy(&lock); // 销毁互斥锁
    return 0;
}
5.4.2 条件变量

条件变量用于线程间的条件同步,一个线程可以等待某个条件满足,另一个线程可以通知条件的变化。

  • 初始化条件变量
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
  • 等待条件变量
pthread_cond_wait(&cond, &mutex);
  • 发送条件信号
pthread_cond_signal(&cond);
示例:
#include <pthread.h>
#include <stdio.h>

pthread_mutex_t lock;
pthread_cond_t cond;
int ready = 0; // 条件变量的条件

// 线程函数,等待条件满足
void* wait_for_condition(void* arg) {
    pthread_mutex_lock(&lock);
    while (!ready) {
        pthread_cond_wait(&cond, &lock); // 等待条件变量
    }
    printf("Condition met, proceeding...\n");
    pthread_mutex_unlock(&lock);
    return NULL;
}

// 线程函数,改变条件并通知
void* signal_condition(void* arg) {
    pthread_mutex_lock(&lock);
    ready = 1;
    pthread_cond_signal(&cond); // 发送条件信号
    pthread_mutex_unlock(&lock);
    return NULL;
}

int main() {
    pthread_t thread1, thread2;

    pthread_mutex_init(&lock, NULL);
    pthread_cond_init(&cond, NULL);

    pthread_create(&thread1, NULL, wait_for_condition, NULL);
    pthread_create(&thread2, NULL, signal_condition, NULL);

    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);

    pthread_mutex_destroy(&lock);
    pthread_cond_destroy(&cond);
    return 0;
}

6. 实战:生产者-消费者问题

生产者-消费者问题是多线程编程中的经典问题,生产者线程生成数据,消费者线程消费数据,两者通过缓冲区进行通信,需要使用互斥锁和条件变量来确保线程同步。

6.1 问题描述

  • 生产者:生产数据并放入缓冲区,如果缓冲区满则等待。
  • 消费者:从缓冲区取出数据并消费,如果缓冲区空则等待。

6.2 解决方案

使用互斥锁和条件变量解决生产者-消费者问题:

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

#define BUFFER_SIZE 10

int buffer[BUFFER_SIZE]; // 缓冲区
int count = 0; // 缓冲区中的数据量

pthread_mutex_t lock;
pthread_cond_t not_empty;
pthread_cond_t not_full;

void* producer(void* arg) {
    int i = 0;
    while (1) {
        pthread_mutex_lock(&lock);
        
        while (count == BUFFER_SIZE) {
            pthread_cond_wait(&not_full, &lock); // 缓冲区满,等待
        }

        buffer[count++] = i;
        printf("Produced: %d\n", i++);
        
        pthread_cond_signal(&not_empty); // 通知消费者缓冲区不空
        pthread_mutex_unlock(&lock);
        
        sleep(1); // 模拟生产时间
    }
    return NULL;
}

void* consumer(void* arg) {
    int item;
    while (1) {
        pthread_mutex_lock(&lock);
        
        while (count == 0) {
            pthread_cond_wait(&not_empty, &lock); // 缓冲区空,等待
        }

        item = buffer[--count];
        printf("Consumed: %d\n", item);
        
        pthread_cond_signal(&not_full); // 通知生产者缓冲区不满
        pthread_mutex_unlock(&lock);
        
        sleep(1); // 模拟消费时间
    }
    return NULL;
}

int main() {
    pthread_t prod, cons;

    pthread_mutex_init(&lock, NULL);
    pthread_cond_init(&not_empty, NULL);
    pthread_cond_init(&not_full, NULL);

    pthread_create(&prod, NULL, producer, NULL);
    pthread_create(&cons, NULL, consumer, NULL);

    pthread_join(prod, NULL);
    pthread_join(cons, NULL);

    pthread_mutex_destroy(&lock);
    pthread_cond_destroy(&not_empty);
    pthread_cond_destroy(&not_full);

    return 0;
}

在这个示例中,我们创建了一个生产者线程和一个消费者线程,生产者线程不断生成数据并放入缓冲区,而消费者线程不断从缓冲区取出数据并消费。通过互斥锁和条件变量,确保了生产者和消费者之间的正确同步。

7. 进程和线程在应用中的选择

在实际应用中,选择使用进程还是线程取决于具体的需求和场景。

  • 进程适用于需要高隔离性和安全性的任务,如独立的服务或后台进程。
  • 线程适用于需要高并发和低开销的任务,如多线程服务器或实时数据处理。

通过合理地使用进程和线程,可以提高程序的效率和性能,实现更高效的并发执行。

8. 总结

进程和线程是操作系统中实现并发执行的两种主要方式,各有优缺点和适用场景。通过理解它们的基本概念和特点,以及掌握相关的编程技巧和同步机制,可以编写出高效的并发程序,充分利用多核处理器的计算能力。

  • 进程具有独立的地址空间和资源,适用于需要高隔离性和安全性的任务。
  • 线程共享进程的地址空间和资源,适用于需要高并发和低开销的任务。
  • POSIX线程库(pthreads)提供了强大的多线程编程接口,可以方便地创建和管理线程,实现线程间的同步和通信。

通过上述详解,相信你对C语言中的进程和线程有了更深入的理解,并能够在实际编程中灵活运用。

9. 结束语

  1. 本节内容已经全部介绍完毕,希望通过这篇文章,大家对C语言进程和线程详解有了更深入的理解和认识。
  2. 感谢各位的阅读和支持,如果觉得这篇文章对你有帮助,请不要吝惜你的点赞和评论,这对我们非常重要。再次感谢大家的关注和支持!点我关注❤️

相关文章:

  • 指针的神秘探险:从入门到精通的奇幻之旅 !

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

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

相关文章

Halcon灰度图像的形态学运算

Halcon灰度图像的形态学运算 本文介绍的算子的输入类型是灰度的Image图像。 1. 灰度图像与区域的区别 基于区域的形态学运算与基于灰度图像的形态学运算的根本区别在于&#xff0c;二者输入的对象不同。前者输入的是一些区域&#xff0c;并且这些区域是经过闽值处理的二值图…

微信小程序在线客服源码系统全端通吃 带完整的安装代码包以及搭建部署教程

系统概述 “微信小程序在线客服源码系统全端通吃”是一款集智能客服、人工客服、消息管理、数据分析等功能于一体的综合性解决方案。该系统基于微信小程序平台开发&#xff0c;支持全端接入&#xff08;包括Web、App、小程序等&#xff09;&#xff0c;实现多渠道客户服务的无…

英国海外媒体通稿宣发:顶级媒体宣发

1.伦敦日报londonjournal 作为英国首都的权威日报&#xff0c;伦敦日报一直是英国新闻界的佼佼者。它详尽报道伦敦及英国各地的政治、经济、社会、文化、体育等各方面的新闻&#xff0c;深受读者喜爱。 2.英国先驱报ukherald 英国先驱报是一份全国性日报&#xff0c;以深度分…

源码构建LAMP

目录 一、安装Apache 二、安装Mysql 三、安装PHP 四、安装论坛 一、安装Apache 1.cd 到opt目录下面&#xff0c;将压缩包拉进Xhell 2.解压缩apr和httpd压缩包 tar xf apr-1.6.2.tar.gz tar xf apr-util-1.6.0.tar.gz tar xf httpd-2.4.29.tar.bz2 3.将apr-1.6.2 移动到ht…

playbook(剧本)基本应用、playbook常见语法、playbook和ansible操作的编排

playbook(剧本): 是ansible⽤于配置,部署,和管理被控节点的剧本。⽤ 于ansible操作的编排。 使⽤的格式为yaml格式 一、YMAL格式 以.yaml或.yml结尾 ⽂件的第⼀⾏以 "---"开始&#xff0c;表明YMAL⽂件的开始(可选的) 以#号开头为注释 列表中的所有成员都开始于…

开放式耳机原理是什么?它通过不入耳的方式带来动感音乐

开放式耳机的原理主要分为两种类型&#xff1a;气传导和骨传导。 气传导耳机&#xff1a;这种耳机的工作原理依赖于空气作为声音传播的介质。具体来说&#xff0c;音频设备通过耳机线将电信号传递到耳机&#xff0c;耳机内部的驱动单元&#xff08;通常是动圈式或平衡电枢式&am…

未开启语音助手时,远程控制功能助你快速在家找回手机!

完成一整天的大扫除之后&#xff0c;顺手就想摸出手机刷一下短视频&#xff0c;但摸不到。干了一天活&#xff0c;手机放哪里都忘了&#xff0c;于是不得不在几个房间之间寻找。 但找过手机的都知道&#xff0c;越找越是找不到。糟糕的是前几天我嫌麻烦&#xff0c;把语音助手…

<数据集>铝型材缺陷识别数据集<目标检测>

数据集格式&#xff1a;VOCYOLO格式 图片数量&#xff1a;1885张 标注数量(xml文件个数)&#xff1a;1885 标注数量(txt文件个数)&#xff1a;1885 标注类别数&#xff1a;10 标注类别名称&#xff1a;[budaodian, tufen, loudi, qikeng, pengshang, tucengkailie, zangdi…

光伏模拟器的应用

太阳能光伏 (PV) 模拟器是一种可编程电源&#xff0c;用于模拟太阳能电池板。模拟器具有快速瞬态响应&#xff0c;可响应负载条件的变化并保持电压-电流特性的输出。 用户可以根据系统规格定义太阳能电池板配置&#xff0c;并通过选择环境条件来选择适当的环境条件进行模拟。用…

通风采光排烟天窗现行七本图集概览

在建筑设计与施工中&#xff0c;通风采光排烟天窗作为优化室内环境的重要设备&#xff0c;选择合适的型号及合理应用至关重要。当前市场上存在着多本标准化、规范化的通风采光排烟天窗图集&#xff0c;为设计师、工程师及施工单位、通风采光排烟天窗生产厂家提供丰富的参考资源…

如何有效找到目标客户群体?

在激烈的市场竞争中&#xff0c;找到并锁定目标客户群体是企业成功的关键。以下是几种有效的策略&#xff0c;帮助您精准定位并吸引目标客户。 1. 明确市场定位与客户画像 首先&#xff0c;企业需要明确市场定位&#xff0c;并绘制详细的客户画像&#xff0c;包括年龄、性别、…

LeetCode合并两个有序链表

题目描述&#xff1a; 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 1&#xff1a; 输入&#xff1a;l1 [1,2,4], l2 [1,3,4] 输出&#xff1a;[1,1,2,3,4,4] 示例 2&#xff1a; 输入&#xff1a;l1 [], l2…

以FLV解复用为例详解开源库FFmpeg中解复用器的源码逻辑及处理流程

目录 1、FFmpeg简介 2、FLV文件格式介绍 3、注册解复用器 4、解复用器的处理 4.1、AVFormatContext 4.1.1、AVClass 4.1.2、AVOption 4.1.3 AVDictionary—AV字典 4.1.4、AVIOContext 4.1.4.1、URLProtocol 4.1.4.2、AVIOContext的初始化及获取 4.1.5、AVInputF…

基于vue篮球联盟管理系统pf

TOC springboot476基于vue篮球联盟管理系统pf 第1章 绪论 1.1 课题背景 二十一世纪互联网的出现&#xff0c;改变了几千年以来人们的生活&#xff0c;不仅仅是生活物资的丰富&#xff0c;还有精神层次的丰富。在互联网诞生之前&#xff0c;地域位置往往是人们思想上不可跨域…

Python入门教程(超详细)

《网络安全自学教程》 Python是一种「基于C语言」实现的&#xff0c;「开源」的&#xff0c;「面向对象的」的&#xff0c;「动态数据类型」的「解释型」语言。 Python的语法「简单」且「优雅」&#xff0c;相对于其他语言学习「难度较低」&#xff0c;入门非常快。 Python的…

【C++】使用红黑树封装map与set

文章目录 1. 源码分析2. 调整红黑树的结构搭建map、set3. 红黑树的迭代器3.1 普通迭代器3.2 const迭代器3.3 map的operator[ ] 4. 完整代码4.1 RBTree4.2 MyMap4.3 MySet 对于map与set&#xff0c;它们一个是KV模型&#xff0c;一个是K模型&#xff0c;那我们要写两个红黑树吗&…

基于Springboot网上蛋糕售卖店管理系统的设计与实现--论文pf

TOC springboot504基于Springboot网上蛋糕售卖店管理系统的设计与实现--论文pf 第1章 绪论 1.1选题动因 当前的网络技术&#xff0c;软件技术等都具备成熟的理论基础&#xff0c;市场上也出现各种技术开发的软件&#xff0c;这些软件都被用于各个领域&#xff0c;包括生活和…

解决springboot中Aspect注解不生效问题

如下图所示&#xff0c;配置了一个注解类型的Aspect&#xff0c;结果一直不生效 运行结果可以看到&#xff0c;其他非注解类型的Aspect都顺利执行了&#xff0c;但是这个注解的切面就是没有执行 当时也在网上搜了半天&#xff0c;包括在启动类增加配置&#xff0c;接口都要加上…

Java语言程序设计基础篇_编程练习题**16.30(模式识别:连续四个相同的数)

目录 题目&#xff1a;**16.30&#xff08;模式识别&#xff1a;连续四个相同的数&#xff09; 习题思路 代码示例 结果展示 题目&#xff1a;**16.30&#xff08;模式识别&#xff1a;连续四个相同的数&#xff09; 为编程练习题8.19编写一个GUI程序。让用户在6行7列的网格的…

实时手势识别(2)- 基于关键点分类实现零样本图片的任意手势的识别

目录 前言 1.实现效果 2.关键点分类网络 3.KPNet训练测试数据准备 4.训练结果 4.1训练过程可视化 4.2验证集上的混淆矩阵 4.测试结果 4.1不同规模模型的测试结果对比 4.2分类结果投影到第一象限 4.3测试集上的混淆矩阵 4.4 二义性手势结果 4.5视频实测 5.零样本的…