C语言线程编程深度解析

news2024/11/20 11:20:25

在这里插入图片描述

文章目录

    • 前言
    • 一、线程基础概念
      • 1. 什么是线程?
      • 2. 线程与进程的区别
    • 二、POSIX线程库(pthread)
      • 1. pthread简介
      • 2. 编译与链接
      • 3. 创建线程
        • 示例代码:
      • 4. 线程同步
        • 互斥锁(Mutex)
          • 示例代码:
        • 条件变量(Condition Variable)
          • 示例代码:
      • 5. 死锁与解决方案
        • 死锁的产生条件

🌈你好呀!我是 山顶风景独好
🎈欢迎踏入我的博客世界,能与您在此邂逅,真是缘分使然!😊
🌸愿您在此停留的每一刻,都沐浴在轻松愉悦的氛围中。
📖这里不仅有丰富的知识和趣味横生的内容等您来探索,更是一个自由交流的平台,期待您留下独特的思考与见解。🌟
🚀让我们一起踏上这段探索与成长的旅程,携手挖掘更多可能,共同进步!💪✨

前言

在现代计算中,多线程编程是提高应用程序效率和响应能力的关键技术之一。C语言,作为系统编程的基石,提供了强大的工具来直接管理线程,尽管它没有内置的线程支持,但通过POSIX线程库(通常称为pthread),开发者可以在Unix-like系统(如Linux)上实现多线程功能。本文将深入探讨C语言中的线程编程,包括基本概念、线程创建、同步、互斥、死锁处理等多个方面,并通过丰富的示例代码来加深理解。

一、线程基础概念

1. 什么是线程?

线程是进程中的一个执行单元,负责执行程序中的代码。一个进程可以包含多个线程,它们共享进程的地址空间、全局变量等资源,但拥有各自独立的栈和程序计数器。这种设计使得线程间的通信和数据共享比进程间更为高效。

2. 线程与进程的区别

  • 资源拥有:进程是资源分配的基本单位,拥有独立的内存空间和系统资源;线程是CPU调度的基本单位,不拥有资源,但可与同属进程的其他线程共享资源。
  • 开销:创建和销毁一个进程的开销远大于线程,因为进程涉及内存空间的分配和回收、系统资源的分配等;而线程的创建和销毁只需保存和恢复少量的寄存器内容。
  • 并发性:在同一时刻,多个进程可以并发执行,但进程间通信较为复杂;同一进程内的多个线程也可以并发执行,且通信更为简便。

二、POSIX线程库(pthread)

1. pthread简介

POSIX线程(pthread)是IEEE为要求操作系统兼容设计的一系列API标准的总称,旨在提供跨平台的线程支持。在Linux系统中,pthread是实现多线程编程的标准方式。

2. 编译与链接

使用pthread库的程序需要在编译时链接pthread库。例如,对于名为mythread.c的源文件,编译命令为:

gcc -o mythread mythread.c -lpthread

3. 创建线程

pthread提供了一个核心函数pthread_create用于创建新线程。其基本原型为:

#include <pthread.h>  
  
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:传递给线程函数的参数。
示例代码:
#include <stdio.h>  
#include <stdlib.h>  
#include <pthread.h>  
  
void *print_message_function(void *ptr) {  
    char *message;  
    message = (char *) ptr;  
    printf("%s \n", message);  
    pthread_exit(NULL);    // 线程退出  
}  
  
int main() {  
    pthread_t thread1, thread2; // 线程的标识符  
    const char *message1 = "Thread 1";  
    const char *message2 = "Thread 2";  
    int iret1, iret2;  
  
    // 创建线程  
    iret1 = pthread_create(&thread1, NULL, print_message_function, (void*) message1);  
    if (iret1) {  
        fprintf(stderr, "Error - pthread_create() return code: %d\n", iret1);  
        exit(EXIT_FAILURE);  
    }  
  
    iret2 = pthread_create(&thread2, NULL, print_message_function, (void*) message2);  
    if (iret2) {  
        fprintf(stderr, "Error - pthread_create() return code: %d\n", iret2);  
        exit(EXIT_FAILURE);  
    }  
  
    // 等待线程结束  
    pthread_join(thread1, NULL);  
    pthread_join(thread2, NULL);  
  
    printf("Thread 1 returns: %d\n", iret1);  
    printf("Thread 2 returns: %d\n", iret2);  
    exit(EXIT_SUCCESS);  
}

在这个例子中,我们创建了两个线程,每个线程执行print_message_function函数,打印不同的消息。pthread_join函数用于等待线程执行完毕,确保主线程不会提前退出。

4. 线程同步

在多线程程序中,线程间的同步是至关重要的,它确保线程按照预期的顺序执行,避免数据竞争和不一致。

互斥锁(Mutex)

互斥锁是pthread中用于保护共享资源不被多个线程同时访问的机制。通过锁定(lock)和解锁(unlock)操作,确保同一时刻只有一个线程能访问特定资源。

示例代码:
#include <stdio.h>  
#include <stdlib.h>  
#include <pthread.h>  
  
pthread_mutex_t lock;  
  
void *print_message_function(void *ptr) {  
    pthread_mutex_lock(&lock);  
  
    char *message;  
    message = (char *) ptr;  
    printf("%s \n", message);  
  
    pthread_mutex_unlock(&lock);  
    pthread_exit(NULL);  
}  
  
int main() {  
    pthread_t thread1, thread2;  
    const char *message1 = "Thread 1";  
    const char *message2 = "Thread 2";  
    int iret1, iret2;  
  
    pthread_mutex_init(&lock, NULL);  
  
    iret1 = pthread_create(&thread1, NULL, print_message_function, (void*) message1);  
    if (iret1) {  
        fprintf(stderr, "Error - pthread_create() return code: %d\n", iret1);  
        exit(EXIT_FAILURE);  
    }  
  
    iret2 = pthread_create(&thread2, NULL, print_message_function, (void*) message2);  
    if (iret2) {  
        fprintf(stderr, "Error - pthread_create() return code: %d\n", iret2);  
        exit(EXIT_FAILURE);  
    }  
  
    pthread_join(thread1, NULL);  
    pthread_join(thread2, NULL);  
  
    pthread_mutex_destroy(&lock);  
  
    exit(EXIT_SUCCESS);  
}

在这个例子中,我们使用了pthread_mutex_lock和pthread_mutex_unlock来确保两个线程不会同时打印消息,避免了输出的混乱。

条件变量(Condition Variable)

条件变量允许线程在某些条件不满足时等待,并在条件满足时被唤醒。它与互斥锁配合使用,以实现更复杂的线程同步逻辑。

示例代码:
#include <stdio.h>  
#include <stdlib.h>  
#include <pthread.h>  
  
pthread_mutex_t lock;  
pthread_cond_t cond;  
int ready = 0;  
  
void *wait_function(void *ptr) {  
    pthread_mutex_lock(&lock);  
    while (!ready) {  
        pthread_cond_wait(&cond, &lock);  
    }  
    printf("Thread is unblocked.\n");  
    pthread_mutex_unlock(&lock);  
    pthread_exit(NULL);  
}  
  
void *signal_function(void *ptr) {  
    sleep(1); // 模拟某些工作  
    pthread_mutex_lock(&lock);  
    ready = 1;  
    printf("Setting ready to 1.\n");  
    pthread_cond_signal(&cond);  
    pthread_mutex_unlock(&lock);  
    pthread_exit(NULL);  
}  
  
int main() {  
    pthread_t thread1, thread2;  
    int iret1, iret2;  
  
    pthread_mutex_init(&lock, NULL);  
    pthread_cond_init(&cond, NULL);  
  
    iret1 = pthread_create(&thread1, NULL, wait_function, NULL);  
    if (iret1) {  
        fprintf(stderr, "Error - pthread_create() return code: %d\n", iret1);  
        exit(EXIT_FAILURE);  
    }  
  
    iret2 = pthread_create(&thread2, NULL, signal_function, NULL);  
    if (iret2) {  
        fprintf(stderr, "Error - pthread_create() return code: %d\n", iret2);  
        exit(EXIT_FAILURE);  
    }  
  
    pthread_join(thread1, NULL);  
    pthread_join(thread2, NULL);  
  
    pthread_mutex_destroy(&lock);  
    pthread_cond_destroy(&cond);  
  
    exit(EXIT_SUCCESS);  
}

在这个例子中,wait_function线程等待条件变量cond,而signal_function线程在准备好后发送信号,唤醒等待线程。

5. 死锁与解决方案

死锁是指两个或多个线程在互相等待对方释放资源,导致它们之间形成了一种循环等待的情况,从而使得这些线程都无法继续执行。死锁是多线程编程中需要特别注意和避免的问题,因为它会导致程序挂起,无法正常工作。

死锁的产生条件

死锁通常发生在满足以下四个条件时:

  1. 互斥条件:至少有一个资源必须处于非共享模式,即一次只有一个线程能使用资源。如果其他线程请求该资源,则请求线程必须等待。
    持有并等待条件:一个线程至少持有一个资源,并等待获取额外的资源,而该资源可能被其他线程持有。
  2. 不可抢占条件:资源一旦被分配,就无法被强制从该线程中取走,必须由持有它的线程主动释放。
  3. 循环等待条件:存在一个等待循环,每个线程都等待下一个线程所持有的资源。
    死锁的解决方案

为了避免死锁,可以采取以下几种策略:

  1. 预防策略:
    • 破坏互斥条件:使资源尽可能多地共享,从而减少死锁的可能性。
      破坏持有并等待条件:要求所有线程在开始时一次性请求所有所需资源,或者要求线程在获取资源时不得持有其他资源。
    • 破坏不可抢占条件:允许资源被抢占,即当一个线程持有的资源被另一个线程更需要时,可以强制剥夺该资源。
    • 破坏循环等待条件:采用资源有序分配法(Resource Ordering),给每个资源分配一个唯一的编号,线程必须按编号的顺序请求资源。
  2. 避免策略:
    使用银行家算法等动态地检查资源分配的安全性,只允许那些不会形成死锁的资源请求。
  3. 检测策略:
    定期检测系统中的资源分配情况,一旦发现死锁,立即采取措施解除。
  4. 恢复策略:
    • 终止一个或多个线程以打破死锁循环。可以选择终止代价最小的线程,如优先级最低的线程或执行时间最短的线程。
    • 回滚线程,使其放弃所占有的资源,回退到某个安全状态。
    • 增加资源,以满足被阻塞线程的需求,从而解除死锁。

示例:预防死锁的资源有序分配法
以下是一个使用资源有序分配法预防死锁的简化示例:

#include <stdio.h>  
#include <pthread.h>  
#include <stdlib.h>  
  
#define NUM_RESOURCES 2  
  
pthread_mutex_t resource_locks[NUM_RESOURCES];  
int resources[NUM_RESOURCES] = {1, 1}; // 假设资源初始都可用  
  
void *thread_function(void *arg) {  
    int thread_id = *(int *)arg;  
    int first_resource = thread_id % NUM_RESOURCES;  
    int second_resource = (thread_id + 1) % NUM_RESOURCES;  
  
    // 按照资源编号的顺序请求资源  
    pthread_mutex_lock(&resource_locks[first_resource]);  
    while (resources[first_resource] == 0) {  
        pthread_mutex_unlock(&resource_locks[first_resource]); // 释放锁以避免忙等  
        sched_yield(); // 让出CPU,等待资源可用  
        pthread_mutex_lock(&resource_locks[first_resource]);  
    }  
    resources[first_resource] = 0; // 占用资源  
  
    pthread_mutex_lock(&resource_locks[second_resource]);  
    while (resources[second_resource] == 0) {  
        pthread_mutex_unlock(&resource_locks[second_resource]);  
        pthread_mutex_unlock(&resource_locks[first_resource]); // 释放已占有的资源,避免死锁  
        sched_yield();  
        pthread_mutex_lock(&resource_locks[first_resource]);  
        pthread_mutex_lock(&resource_locks[second_resource]);  
    }  
    resources[second_resource] = 0; // 占用资源  
  
    // 执行某些操作  
    printf("Thread %d has acquired resources %d and %d\n", thread_id, first_resource, second_resource);  
  
    // 释放资源  
    resources[second_resource] = 1;  
    pthread_mutex_unlock(&resource_locks[second_resource]);  
    resources[first_resource] = 1;  
    pthread_mutex_unlock(&resource_locks[first_resource]);  
  
    pthread_exit(NULL);  
}  
  
int main() {  
    pthread_t threads[4];  
    int thread_ids[4] = {0, 1, 2, 3};  
  
    for (int i = 0; i < NUM_RESOURCES; i++) {  
        pthread_mutex_init(&resource_locks[i], NULL);  
    }  
  
    for (int i = 0; i < 4; i++) {  
        pthread_create(&threads[i], NULL, thread_function, &thread_ids[i]);  
    }  
  
    for (int i = 0; i < 4; i++) {  
        pthread_join(threads[i], NULL);  
    }  
  
    for (int i = 0; i < NUM_RESOURCES; i++) {  
        pthread_mutex_destroy(&resource_locks[i]);  
    }  
  
    return 0;  
}

✨ 这就是今天要分享给大家的全部内容了,我们下期再见!😊
🏠 我在CSDN等你哦!我的主页😍

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

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

相关文章

SpringBoot代码实战(MyBatis-Plus+Thymeleaf)

构建项目 修改pom.xml文件&#xff0c;添加其他依赖以及设置 <!--MyBatis-Plus依赖--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-spring-boot3-starter</artifactId><version>3.5.6</version><…

智源研究院与百度达成战略合作 共建AI产研协同生态

2024年9月24日&#xff0c;北京智源人工智能研究院&#xff08;简称“智源研究院”&#xff09;与北京百度网讯科技有限公司&#xff08;简称“百度”&#xff09;正式签署战略合作协议&#xff0c;双方将充分发挥互补优势&#xff0c;在大模型等领域展开深度合作&#xff0c;共…

共享打印机无法创建打印作业原因分析及解决方法

在日常办公和生活中&#xff0c;打印机是不可或缺的重要设备。然而&#xff0c;有时在添加打印机的过程中&#xff0c;经常会遇各种问题。今天有个小伙伴问我在访问共享打印机时提示“无法创建打印作业”怎么回事&#xff1f;今天小编就教大家共享打印机无法创建打印作业原因分…

多表查询。

一、多表查询 select * from 表名,表名; select * from 表名,表名 where 条件; 二、内连接 隐式 select 字段列表 from 表1,表2 where 条件; 显示 select 字段列表 from 表1 INNER JOIN 表2 on 条件; 三、外连接 1.左外连接 select 字段列表 from 表1 left [outer] join 表…

接口自动化测试框架详解(pytest+allure+aiohttp+ 用例自动生成)

&#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 近期准备优先做接口测试的覆盖&#xff0c;为此需要开发一个测试框架&#xff0c;经过思考&#xff0c;这次依然想做点儿不一样的东西。 接口测试是比较讲究效…

【C++】STL--string(下)

1.string类对象的修改操作 erase&#xff1a;指定位置删除 int main() {string str1("hello world");str1.push_back(c);//尾插一个ccout << str1 << endl;string str2;str2.append("hello"); // 在str后追加一个字符"hello"cout…

openwrt固件选择推荐一:kwrt

前言 本文将推荐第一个openwrt固件Kwrt&#xff0c;帮助openwrt新手用户快速构建自己固件。下篇会推荐第二个Openwrt优秀固件。 一.openwrt定制系统&#xff1a;Kwrt github项目地址&#xff1a;点击跳转 支持300设备 1.提供在线定制页面&#xff0c;定制预装软件 固件在…

如何在 Windows 台式机或笔记本电脑上恢复未保存的 Excel 文件

您的汗水很容易化为灰烬&#xff0c;如果您没有保存长时间编写的项目报告或电子表格&#xff0c;这可能会令人心碎。丢失 Windows PC 上未保存的 Excel 文件可能是导致这种情况的原因。但您不应该惊慌。仍然有机会恢复未保存的 Excel 文件。 在本指南中&#xff0c;我们将向您…

failed to load steamui.dll的多种处理方法,steamui.dll的作用

在使用Steam平台时&#xff0c;不少玩家可能会遇到“failed to load steamui.dll”这样令人头疼的错误提示。这个错误会阻碍Steam客户端的正常运行&#xff0c;影响我们享受游戏和Steam平台的各种服务。不过&#xff0c;不必过于担心&#xff0c;因为有多种方法可以尝试解决这个…

车位租赁系统的设计与实现

摘 要 传统信息的管理大部分依赖于管理人员的手工登记与管理&#xff0c;然而&#xff0c;随着近些年信息技术的迅猛发展&#xff0c;让许多比较老套的信息管理模式进行了更新迭代&#xff0c;车位信息因为其管理内容繁杂&#xff0c;管理数量繁多导致手工进行处理不能满足广…

3d gaussian splatting公式推导

1. 离散公式推导 nerf中连续的积分渲染公式是&#xff1a; 其中被遮挡率&#xff1a; 那么转换为离散公式后有&#xff1a; 其中&#xff0c;代表j时刻的时间差&#xff0c;将其带入渲染公式&#xff1a; 设透明度 则被遮挡率 有 而gaussian-splating的公式与ner…

CNN-LSTM预测 | MATLAB实现CNN-LSTM卷积长短期记忆神经网络时间序列预测

CNN-LSTM预测 | MATLAB实现CNN-LSTM卷积长短期记忆神经网络时间序列预测 目录 CNN-LSTM预测 | MATLAB实现CNN-LSTM卷积长短期记忆神经网络时间序列预测预测效果基本介绍模型描述程序设计参考资料预测效果 基本介绍 本次运行测试环境MATLAB2020b 提出一种包含卷积神经网络和长短…

windows11环境安装lua及luarocks(踩坑篇)

一、lua安装及下载 官方地址&#xff1a; Lua Binaries Download 从这里就有坑了&#xff0c;下载后先解压win64_bin.zip&#xff0c;之后解压lib&#xff0c;用lib中的文件替换win64的&#xff0c;并把include文件夹复制过去&#xff0c;之后复制并重命名lua54&#xff0c;方…

面试知识点总结篇四

一、计算机网络 概念&#xff1a;互连的、自治的计算机系统的集合组成&#xff1a;硬件、软件、协议。功能组成&#xff1a;通信子网&#xff08;物理层、数据链路层、网络层&#xff09;、资源子网&#xff08;会话层、表示层、应用层&#xff09;分别有广域网、城域网、局域…

2024年软考网络工程师中级题库

1【考生回忆版】以下不属于5G网络优点的是&#xff08;A) A.传输过程中消耗的资源少&#xff0c;对设备的电池更友好 B.支持大规模物联网&#xff0c;能够连接大量低功耗设备&#xff0c;提供更高效的管理 C.引入了网络切片技术&#xff0c;允许将物理网络划分为多个虚拟网络…

数据分析:Python语言网络图绘制

文章目录 介绍加载R包类别导入数据下载数据画图介绍 网络图是一种图形表示法,用于展示实体之间的关系。在不同的领域中,网络图有着不同的含义和用途:在生物学中,网络图可以用来表示生物分子之间的相互作用,如蛋白质相互作用网络。 加载R包 import pandas as pd import …

Docker 安装 Apache(图文教程)

Apache HTTP服务器(简称Apache)是一个开源的、跨平台的Web服务器软件,由Apache软件基金会开发和维护。Apache HTTP服务器是世界上最流行的Web服务器软件之一,被广泛用于互联网上的网站和应用程序。 一、拉取镜像 docker pull httpd:latest二、运行容器 Apache的默认端口是…

计算机毕业设计之:宠物服务APP的设计与实现(源码+文档+讲解)

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…

Spring Boot 点餐系统:餐饮界的技术革新

第四章 系统设计 4.1 系统体系结构 网上点餐系统的结构图4-1所示&#xff1a; 图4-1 系统结构 模块包括主界面&#xff0c;首页、个人中心、用户管理、美食店管理、美食分类管理、美食信息管理、美食订单管理、美食评价管理、系统管理等进行相应的操作。 登录系统结构图&…

加入AI新引擎,华为数据中台全面智能化升级

作者 | 曾响铃 文 | 响铃说 根据IDC 报告&#xff0c;截至2022年中国数据规模已经达到23.88ZB&#xff0c;预计2027年将达到76.6ZB&#xff0c;五年年均增长速度将达到26.3%。在这样的背景下&#xff0c;如何进一步挖掘数据价值、提高数据应用效率&#xff0c;成为企业们普遍…