c++ 线程间通信方式

线程同步和线程互斥

  • 互斥

某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的,线程间不需要知道彼此的存在。

  • 同步

在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问,线程间知道彼此的存在。在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源

线程间通信方式

  • 两个进程间的两个线程通信,相当于进程间通信:
    信号量, socket网络连接, 共享内存 ,管道,共享文件
  • 一个进程中的两个线程间通信方式:
    1.互斥锁
    mutex;
    lock_guard (在构造函数里加锁,在析构函数里解锁)
    unique_lock 自动加锁、解锁
    atomic 基本类型的原子操作
    参考链接:
    std::unique_lock与std::lock_guard区别
    C++11 std::unique_lock与std::lock_guard区别及多线程应用实例
    C11:std::unique_lock和std::lock_guard的区别
    2.条件变量condition_variable
    关于互斥锁和条件变量:
    互斥量可以保护共享数据的修改,如果线程正在等待共享数据的某个条件出现,仅用互斥量的话就需要反复对互斥对象锁定解锁,以检查值的变化,这样将频繁查询的效率非常低。
    条件变量可以让等待共享数据条件的线程进入休眠,并在条件达成时唤醒等待线程,提供一种更高效的线程同步方式。条件变量一般和互斥锁同时使用,提供一种更高效的线程同步方式。
    3.信号量
    4.读写锁shared_lock。

信号量、条件变量、互斥量

1.互斥锁是为上锁而优化的;条件变量是为等待而优化的; 信号量既可用于上锁,也可用于等待,因此会有更多的开销和更高的复杂性。

2.互斥锁,条件变量都只用于同一个进程的各线程间,而信号量可用于不同进程间的同步。当信号量用于进程间同步时,要求信号量建立在共享内存区。

3.读写锁与互斥量类似,不过读写锁允许更高的并行性。读写锁也叫做共享-独占锁,当读写锁以读模式锁住时,它是以共享模式锁住的;当他以写模式锁住时,它是以独占模式锁住的。

进程、线程、协程

1.进程有自己独立的堆和栈,都不共享,是操作系统调度的基本单位

2.线程有自己独立的栈和共享的堆,是cpu调度的基本单位

3.协程有自己独立的栈和共享的堆,由程序员在代码里显示调度。

协程的优点:

  • 没有线程切换的开销,执行销量高;
  • 只有一个线程,共享资源不加锁。

多进程和多线程

在这里插入图片描述

  • 需要频繁创建销毁的优先用线程。
    web服务器。来一个建立一个线程,断了就销毁线程。要是用进程,创建和销毁的代价是很难承受的。
  • 需要进行大量计算的优先使用线程
    所谓大量计算,当然就是要消耗很多cpu,切换频繁了,这种情况先线程是最合适的。比如:图像处理、算法处理
  • 强相关的处理用线程,弱相关的处理用进程。
    一般的server需要完成如下任务:消息收发和消息处理。消息收发和消息处理就是弱相关的任务,而消息处理里面可能又分为消息解码、业务处理,这两个任务相对来说相关性就要强多了。因此消息收发和消息处理可以分进程设计,消息解码和业务处理可以分线程设计。
  • 可能扩展到多机分布的用进程,多核分布的用线程。
### C++线程间通信方式C++ 的多线程环境中,线程间通信可以通过多种方法实现。以下是几种主要的线程间通信方式及其特点: #### 1. **互斥锁 (Mutex)** `std::mutex` 是一种常用的同步工具,用于保护共享资源免受多个线程的同时访问。当一个线程锁定互斥锁时,其他试图获取该锁的线程会被阻塞直到锁被释放[^2]。 ```cpp #include <iostream> #include <thread> #include <mutex> std::mutex mtx; // 定义全局互斥锁 void print_block(int n, char c) { std::lock_guard<std::mutex> lock(mtx); // 自动管理锁的生命周期 for (int i = 0; i < n; ++i) { std::cout << c; } std::cout << '\n'; } int main() { std::thread t1(print_block, 50, '*'); std::thread t2(print_block, 50, '$'); t1.join(); t2.join(); return 0; } ``` 上述代码展示了如何通过 `std::mutex` 和 `std::lock_guard` 实现对共享资源的安全访问。 --- #### 2. **条件变量 (Condition Variable)** 条件变量允许线程等待某个特定条件的发生。它通常与互斥锁一起使用,以便在线程之间传递信号。 ```cpp #include <iostream> #include <thread> #include <queue> #include <mutex> #include <condition_variable> std::queue<int> data_queue; std::mutex queue_mutex; std::condition_variable cv; void producer(int id) { int value = 0; while (true) { { std::unique_lock<std::mutex> ul(queue_mutex); data_queue.push(value++); cv.notify_one(); // 唤醒消费者线程 } std::this_thread::sleep_for(std::chrono::milliseconds(10)); } } void consumer(int id) { while (true) { std::unique_lock<std::mutex> ul(queue_mutex); cv.wait(ul, [] { return !data_queue.empty(); }); // 等待数据可用 int value = data_queue.front(); data_queue.pop(); std::cout << "Consumer " << id << ": " << value << "\n"; } } int main() { std::thread p(producer, 1); std::thread c(consumer, 1); p.detach(); c.join(); return 0; } ``` 这段代码演示了生产者-消费者的经典模式,其中条件变量负责协调两个线程的操作。 --- #### 3. **原子变量 (Atomic Variables)** 对于简单的计数器或其他不需要复杂逻辑的数据结构,可以使用 `std::atomic` 提供无锁操作。这有助于减少因加锁带来的开销。 ```cpp #include <iostream> #include <thread> #include <atomic> std::atomic<int> counter{0}; void increment_counter(int num_increments) { for (int i = 0; i < num_increments; ++i) { counter++; } } int main() { const int NUM_THREADS = 10; const int INCREMENTS_PER_THREAD = 100000; std::thread threads[NUM_THREADS]; for (int i = 0; i < NUM_THREADS; ++i) { threads[i] = std::thread(increment_counter, INCREMENTS_PER_THREAD); } for (auto& th : threads) { th.join(); } std::cout << "Final Counter Value: " << counter.load() << "\n"; return 0; } ``` 这里展示了一个基于原子变量的简单计数器示例。 --- #### 4. **共享内存 (Shared Memory)** 虽然严格意义上不属于 C++ 标准库的一部分,但在 POSIX 平台上,可以通过 `shm_open`, `mmap` 等函数创建跨进程或线程的共享内存区域[^3]。然而,在纯 C++线程环境下更推荐使用前三种方式进行通信。 --- ### 总结 C++ 中的线程间通信主要包括互斥锁条件变量以及原子变量等方式。每种方式都有其适用场景:互斥锁适用于保护临界区;条件变量适合于复杂的事件通知机制;而原子变量则提供了轻量级的并发支持。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值