掌握 C 编程中的多线程:深入讲解和高级概念

掌握 c 编程中的多线程:深入讲解和高级概念

介绍:

C 编程中的多线程使开发人员能够充分利用现代多核处理器的潜力,促进单个进程中任务的并发执行。本综合指南探讨了基本的多线程概念、同步机制和高级主题,为每个概念提供了详细的解释和示例代码。

1. 理解线程:

线程是进程内独立的执行序列,允许并发执行任务。了解线程的创建、管理和状态对于有效的多线程处理至关重要。

线程创建:
pthread_create():初始化一个新线程并开始执行。
pthread_join():等待线程终止后再继续。

#include #include 无效*threadFunc(无效*arg){    printf("来自新线程的你好!n");    pthread_exit(NULL);}int main() {    pthread_t tid;    pthread_create(&tid, NULL, threadFunc, NULL);    pthread_join(tid, NULL);    printf("回到主线程.n");    返回0;}

2、同步与互斥:

当多个线程同时访问共享资源时,会出现竞争条件,从而导致不可预测的行为。互斥体、信号量、条件变量等同步机制保证线程安全。

互斥体(互斥):
互斥体提供互斥,一次只允许一个线程访问共享资源。它们可以防止数据损坏并确保行为一致。

#include #include pthread_mutex_t 互斥体 = PTHREAD_MUTEX_INITIALIZER;int 共享变量 = 0;无效*threadFunc(无效*arg){    pthread_mutex_lock(&mutex);    共享变量++;    printf("线程将共享变量增加到:%dn",共享变量);    pthread_mutex_unlock(&mutex);    pthread_exit(NULL);    }int main() {    pthread_t tid;    pthread_create(&tid, NULL, threadFunc, NULL);    pthread_mutex_lock(&mutex);    共享变量--;    printf("主线程将共享变量递减为:%dn",共享变量);    pthread_mutex_unlock(&mutex);    pthread_join(tid, NULL);    返回0;}

信号量:
信号量是用于控制对共享资源的访问并协调多个线程的执行的同步原语。他们维护一个计数来限制同时访问资源的线程数量。

#include #include #include sem_t 信号量;无效*threadFunc(无效*arg){    sem_wait(&信号量);    printf("线程获取信号量");    // 临界区    sem_post(&信号量);    pthread_exit(NULL);}int main() {    pthread_t tid;    sem_init(&信号量, 0, 1); // 用值 1 初始化信号量    pthread_create(&tid, NULL, threadFunc, NULL);    // 主线程    sem_wait(&信号量);    printf("主线程获取信号量");    // 临界区    sem_post(&信号量);    pthread_join(tid, NULL);    返回0;}

3、线程通信:

线程通信有利于线程之间的协调和同步。条件变量允许线程等待满足特定条件。

条件变量:
条件变量使线程能够等待特定条件的发生。它们通常用于生产者-消费者场景,其中线程在继续之前等待数据可用性。

#include #include pthread_mutex_t 互斥体 = PTHREAD_MUTEX_INITIALIZER;pthread_cond_t condVar = PTHREAD_COND_INITIALIZER;int 数据就绪 = 0;无效*生产者(无效*参数){    pthread_mutex_lock(&mutex);    数据就绪=1;    pthread_cond_signal(&condVar);    pthread_mutex_unlock(&mutex);    pthread_exit(NULL);}无效*消费者(无效*参数){    pthread_mutex_lock(&mutex);    while (!dataReady) {        pthread_cond_wait(&condVar, &mutex);    }    printf("消费者:数据已准备好!n");    pthread_mutex_unlock(&mutex);    pthread_exit(NULL);}int main() {    pthread_t 生产者线程,消费者线程;    pthread_create(&生产者线程,NULL,生产者,NULL);    pthread_create(&consumerThread, NULL, 消费者, NULL);    pthread_join(生产者线程,NULL);    pthread_join(consumerThread, NULL);    返回0;}

4. 先进概念:

优先级反转、饥饿、死锁和自旋锁等高级主题对于构建健壮的多线程应用程序至关重要。

优先级反转:
当低优先级线程持有高优先级线程所需的资源时,就会发生优先级反转,从而导致优先级反转。优先级继承协议通过暂时将低优先级线程的优先级提高到高优先级线程的优先级来帮助缓解此问题。

#include #include pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER;无效*highPriorityThread(无效*arg){    pthread_mutex_lock(&mutex1);    pthread_mutex_lock(&mutex2);    // 执行高优先级任务    pthread_mutex_unlock(&mutex2);    pthread_mutex_unlock(&mutex1);    pthread_exit(NULL);}无效*lowPriorityThread(无效*arg){    pthread_mutex_lock(&mutex2);    pthread_mutex_lock(&mutex1);    // 执行低优先级任务    pthread_mutex_unlock(&mutex1);    pthread_mutex_unlock(&mutex2);    pthread_exit(NULL);}int main() {    pthread_t 高优先级,低优先级;    pthread_create(&highPrioTid, NULL, highPriorityThread, NULL);    pthread_create(&lowPrioTid, NULL, lowPriorityThread, NULL);    pthread_join(highPrioTid, NULL);    pthread_join(lowPrioTid, NULL);    返回0;}

饥饿:
当一个线程由于其他线程不断获取所需资源而无法访问这些资源时,就会发生饥饿。公平的调度策略确保所有线程都有公平的资源分配机会,防止饥饿。

#include #include pthread_mutex_t 互斥体 = PTHREAD_MUTEX_INITIALIZER;int 共享资源 = 0;无效*threadFunc(无效*arg){    pthread_mutex_lock(&mutex);    // 增加共享资源    共享资源++;    printf("线程将共享资源增加到:%dn",共享资源);    pthread_mutex_unlock(&mutex);    pthread_exit(NULL);}int main() {    pthread_t tid1, tid2;    // 创建两个线程    pthread_create(&tid1, NULL, threadFunc, NULL);    pthread_create(&tid2, NULL, threadFunc, NULL);    // 等待两个线程完成    pthread_join(tid1, NULL);    pthread_join(tid2, NULL);    // 主线程    pthread_mutex_lock(&mutex);    // 访问共享资源    printf("主线程访问了sharedResource:%dn",sharedResource);    pthread_mutex_unlock(&mutex);    返回0;}

死锁:
当两个或多个线程无限期地等待对方释放它们所需的资源时,就会发生死锁。避免循环等待并实施死锁检测和恢复机制有助于缓解死锁情况。

#include #include pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER;无效*线程1(无效*参数){    pthread_mutex_lock(&mutex1);    pthread_mutex_lock(&mutex2);    // 临界区    pthread_mutex_unlock(&mutex2);    pthread_mutex_unlock(&mutex1);    pthread_exit(NULL);}无效*线程2(无效*参数){    pthread_mutex_lock(&mutex2);    pthread_mutex_lock(&mutex1);  // 潜在的死锁点    // 临界区    pthread_mutex_unlock(&mutex1);    pthread_mutex_unlock(&mutex2);    pthread_exit(NULL);}int main() {    pthread_t tid1,tid2;    pthread_create(&tid1, NULL, thread1, NULL);    pthread_create(&tid2, NULL, thread2, NULL);    pthread_join(tid1, NULL);    pthread_join(tid2, NULL);    返回0;}

自旋锁:
自旋锁是同步原语,其中线程不断轮询资源的可用性。它们对于短关键部分和低争用场景非常有效。

#include #include pthread_spinlock_t 自旋锁;无效*threadFunc(无效*arg){    pthread_spin_lock(&spinlock);    // 临界区    printf("线程获得了自旋锁n");    // 执行一些任务    pthread_spin_unlock(&spinlock);    pthread_exit(NULL);}int main() {    pthread_t tid1, tid2;    pthread_spin_init(&spinlock, 0);    pthread_create(&tid1, NULL, threadFunc, NULL);    pthread_create(&tid2, NULL, threadFunc, NULL);    pthread_join(tid1, NULL);    pthread_join(tid2, NULL);    pthread_spin_destroy(&spinlock);    返回0;}

结论:

掌握 C 编程中的多线程需要深入理解基本概念、同步机制和高级主题。通过深入研究这些概念并探索示例代码,开发人员可以构建健壮、高效且响应迅速的多线程应用程序。持续实践、实验和遵守最佳实践是精通多线程和开发充分利用现代硬件功能的可靠软件系统的关键。

以上就是掌握 C 编程中的多线程:深入讲解和高级概念的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1455331.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
C++框架在游戏开发中的应用
上一篇 2025年12月18日 07:40:22
C 中的可变参数函数
下一篇 2025年12月18日 07:40:38

相关推荐

  • Golang goroutine与channel调试技巧

    使用go run -race检测数据竞争,结合runtime.NumGoroutine监控协程数量,通过pprof分析阻塞调用栈,利用select超时避免永久阻塞,有效排查goroutine泄漏、死锁和数据竞争问题。 Go语言的goroutine和channel是并发编程的核心,但它们也带来了调试上…

    2026年5月10日
    000
  • HTML表单如何实现PWA支持?怎样添加离线功能?

    答案是利用Service Worker缓存资源并结合Background Sync API实现离线提交与自动同步。通过注册Service Worker缓存表单相关文件,拦截提交行为,将离线数据存入IndexedDB,并注册后台同步任务,待网络恢复后由Service Worker自动发送数据,确保提交…

    2026年5月10日
    000
  • C++ 并发编程中内存访问问题及解决方法?

    在 c++++ 并发编程中,共享内存访问问题包括数据竞争、死锁和饥饿。解决方案有:原子操作:确保对共享数据的访问是原子性的。互斥锁:一次只允许一个线程访问临界区。条件变量:线程等待某个条件满足。读写锁:允许多个线程并发读取,但只能允许一个线程写入。 C++ 并发编程中的内存访问问题及解决方案 在多线…

    2026年5月10日
    000
  • Go语言中sync.WaitGroup的深度解析与实践

    sync.WaitGroup是Go语言中用于并发编程的重要同步原语,它允许主协程等待一组子协程执行完毕。本文将深入探讨WaitGroup的工作原理、典型使用模式及其与sync.Mutex等其他同步机制的区别,并通过实际代码示例,帮助读者掌握其在并发控制中的应用,避免常见的误区,确保并发程序的正确性和…

    2026年5月10日
    000
  • C++的atomic是什么_C++11使用std::atomic实现无锁编程的基础

    std::atomic是C++11提供的模板类,用于封装变量并保证其操作的原子性,如int、bool、指针等类型;通过load、store、fetch_add等操作实现线程安全的共享变量访问,避免数据竞争和锁带来的性能开销;常用于无锁编程场景,如计数器累加,提升并发效率。 在C++11中,std::…

    2026年5月10日
    000
  • Go语言中基于Channel的并发快速排序:原理、实现与性能分析

    本文深入探讨了go语言中利用channel实现并发快速排序的机制。我们将分析其代码结构,阐明channel如何作为数据输入输出的管道,以及并发goroutine如何协同工作。同时,文章将重点评估这种实现方式的性能特点,指出其在展示go并发模型优雅性的同时,相比传统排序算法可能存在的性能开销与内存占用…

    2026年5月10日
    100
  • C++框架中网络通信的性能瓶颈及优化方法?

    常见的 c++++ 框架网络通信瓶颈包括:网络延迟、内存管理、同步阻塞和线程并发。优化方法包括:降低延迟(如使用低延迟协议)、优化内存管理(如使用内存池)、消除阻塞(如使用非阻塞 i/o)和管理并发(如使用线程池)。通过实施这些优化,可以显著提高网络性能,如优化基于 boost.asio 的服务器响…

    2026年5月10日
    100
  • Golang协程同步方法 sync.WaitGroup实践

    首先初始化WaitGroup,再通过Add增加计数,每个goroutine执行完调用Done,主线程调用Wait阻塞直至所有任务完成。 在Go语言中,sync.WaitGroup 是一种常用的协程同步机制,用于等待一组并发的goroutine执行完成。它特别适用于主线程需要等待多个子任务结束的场景,…

    2026年5月10日
    000
  • Golang的函数字面量如何使用 讲解匿名函数的定义与调用方式

    Golang的函数字面量如何使用 讲解匿名函数的定义与调用方式Golang的函数字面量如何使用 讲解匿名函数的定义与调用方式Golang的函数字面量如何使用 讲解匿名函数的定义与调用方式Golang的函数字面量如何使用 讲解匿名函数的定义与调用方式

    go语言中的函数字面量(匿名函数)是一种无需命名即可直接定义和使用的函数,它能提升代码灵活性和表达力。1. 它可赋值给变量并调用;2. 可立即执行(iife);3. 可作为参数传递给其他函数;4. 适用于goroutine并发任务;5. 支持闭包,捕获外部变量形成“记忆体”。使用时需注意循环变量捕获…

    2026年5月10日 用户投稿
    100
  • 解决Go并发程序中的死锁问题:深入分析与实践

    本文旨在帮助开发者理解和解决Go并发程序中常见的死锁问题,特别是当程序抛出 “throw: all goroutines are asleep – deadlock!” 错误时。我们将分析导致死锁的常见原因,并提供修改后的代码示例,展示如何通过缓冲通道和runti…

    2026年5月10日
    000
  • 高性能C++框架对比

    在众多高性能 c++++ 框架中,最受欢迎的选择包括 folly、boost.asio 和 qt concurrent。每个框架都有其独特的特性:folly:轻量级、高效,适用于优化异常处理、内存管理和并行处理。boost.asio:跨平台 i/o 库,提供广泛的网络和并行编程功能。qt concu…

    2026年5月10日
    000
  • 深入理解Go语言中多协程与通道的并发模式

    本文探讨Go语言中多个协程同时从一个通道接收数据或向其发送数据的行为。Go语言规范并未明确规定调度顺序,其行为由运行时调度器决定,因此具有非确定性。文章强调了使用通道参数、避免同一协程读写同一通道以及谨慎使用缓冲通道等最佳实践,并通过具体代码示例展示了多写一读和一写多读的并发模式,帮助开发者构建健壮…

    2026年5月10日
    100
  • C++20的同步原语latch和barrier怎么用_C++多线程编程中的栅栏同步机制

    latch是一次性同步工具,用于等待计数归零后释放所有线程,适用于单次等待场景;barrier支持重复使用,允许多轮同步,适合周期性或分阶段的并行任务。 在C++20中,latch 和 barrier 是新增的两种同步原语,用于简化多线程编程中的等待逻辑。它们都属于“栅栏”类同步机制,但用途和行为有…

    2026年5月10日
    000
  • Go语言全局日志器Lumber的配置与使用

    本文将详细介绍在go语言中,如何通过声明包级别变量的方式,实现`github.com/jcelliott/lumber`等日志库的全局访问。这种方法允许在`main`函数外部的任何函数中方便地使用日志器,避免了重复声明,并确保日志器在程序启动时正确初始化,从而提升代码的可维护性和日志管理的便捷性。 …

    2026年5月10日
    000
  • c++怎么使用条件变量condition_variable_c++条件变量同步机制详解

    条件变量需与互斥锁配合使用,实现线程同步。①包含头文件并定义std::condition_variable与std::mutex。②等待线程通过wait(lock, predicate)阻塞,避免虚假唤醒。③通知线程修改共享数据后调用notify_one()或notify_all()唤醒等待线程。④…

    2026年5月10日
    100
  • Go语言中指针赋值的原子性与并发安全

    在go语言中,指针赋值操作并非天然原子性。在并发环境下,若不采取额外同步措施,对共享指针的读写可能导致数据竞争和不一致状态。本文将深入探讨go语言中确保指针赋值并发安全的方法,包括使用`sync.mutex`进行互斥保护,以及在特定场景下利用`sync/atomic`包实现原子操作。同时,也将提及通…

    2026年5月10日
    100
  • 深入理解Go语言中的内存重排序:GOMAXPROCS与并发编程实践

    本文深入探讨go语言中内存重排序现象的观察与机制。通过分析一个go并发代码示例,揭示了go运行时环境,特别是`gomaxprocs`设置(在go 1.5版本之前)如何影响内存重排序的显现。文章强调,在单核环境下,即使存在潜在的重排序可能,也难以被观察到,并指导开发者如何正确理解go的内存模型及其并发…

    2026年5月10日
    000
  • Go语言Channel并发写入:深入理解其内置安全性

    Go语言的Channel是专为并发通信设计的,其内部机制已自动处理了同步问题。当多个Goroutine同时向同一个Channel写入数据时,开发者无需额外使用互斥锁(Mutex)等同步原语,Channel本身就能确保操作的原子性和数据一致性,从而简化了并发编程模型。 Go Channel与并发模型 …

    2026年5月10日
    000
  • C++shared_ptr与多线程环境安全使用方法

    shared_ptr的引用计数操作线程安全,但其管理的对象及shared_ptr实例本身的并发修改需额外同步。多个线程可安全拷贝或销毁shared_ptr,因引用计数增减为原子操作;但若多线程读写shared_ptr指向的对象,则必须通过互斥锁等机制保证对象数据一致性;此外,当多个线程对同一shar…

    2026年5月10日
    000
  • OneDrive跨设备同步,HTML+CSS走到哪写到哪!

    OneDrive通过云同步实现HTML和CSS代码跨设备实时协作。将项目存于OneDrive文件夹并登录账户,可自动同步至所有设备;在Surface Pro 9运行Windows 11环境下,使用Visual Studio Code打开OneDrive中的项目目录,保存即触发后台同步;移动端安装On…

    2026年5月10日
    000

发表回复

登录后才能评论
关注微信