首页 > 其他分享 >线程间通信

线程间通信

时间:2024-11-15 15:19:47浏览次数:1  
标签:std 队列 间通信 线程 消息 Message

使用 锁 + 信号量 + 队列,可以实现 线程间通信。

 

下面是一个 生产者,消费者的例子。

 

#include <iostream>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <chrono>

// 定义一个消息类型
struct Message {
    int data;
};

// 消息队列类
class MessageQueue {
public:
    // 添加消息到队列
    void enqueue(const Message& msg) {
        std::lock_guard<std::mutex> lock(mtx);
        queue.push(msg);
        cv.notify_one(); // 通知等待的线程有新消息
    }

    // 从队列中获取消息,如果队列为空则阻塞
    Message dequeue() {
        std::unique_lock<std::mutex> lock(mtx);
        cv.wait(lock, [this] { return !queue.empty(); }); // 等待直到队列不为空
        Message msg = queue.front();
        queue.pop();
        return msg;
    }

private:
    std::queue<Message> queue;
    std::mutex mtx;
    std::condition_variable cv;
};

// 生产者线程函数
void producer(MessageQueue& mq, int count) {
    for (int i = 0; i < count; ++i) {
        Message msg = {i};
        std::cout << "Produced: " << msg.data << std::endl;
        mq.enqueue(msg);
        std::this_thread::sleep_for(std::chrono::milliseconds(100)); // 模拟生产时间
    }
}

// 消费者线程函数
void consumer(MessageQueue& mq, int count) {
    for (int i = 0; i < count; ++i) {
        Message msg = mq.dequeue();
        std::cout << "Consumed: " << msg.data << std::endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(150)); // 模拟消费时间
    }
}

int main() {
    MessageQueue mq;
    const int messageCount = 10;

    // 启动生产者线程
    std::thread prodThread(producer, std::ref(mq), messageCount);

    // 启动消费者线程
    std::thread consThread(consumer, std::ref(mq), messageCount);

    // 等待线程完成
    prodThread.join();
    consThread.join();

    return 0;
}

 

 

代码解释

  1. Message 结构:
    • 定义一个简单的 Message 结构,包含一个整数数据成员。
  2. MessageQueue 类:
    • std::queue<Message> queue:用于存储消息的队列。
    • std::mutex mtx:互斥锁,用于保护队列的访问。
    • std::condition_variable cv:条件变量,用于线程间的同步。
    • enqueue(const Message& msg):将消息添加到队列,并使用 notify_one 通知等待的消费者线程有新消息。
    • dequeue():从队列中获取消息,如果队列为空则使用 wait 阻塞,直到有新消息可用。
  3. 生产者线程函数 producer:
    • 创建一个 Message 对象,将其添加到消息队列中,并打印生产信息。
    • 使用 std::this_thread::sleep_for 模拟生产时间。
  4. 消费者线程函数 consumer:
    • 从消息队列中获取消息,并打印消费信息。
    • 使用 std::this_thread::sleep_for 模拟消费时间。
  5. main 函数:
    • 创建一个 MessageQueue 对象。
    • 启动生产者线程和消费者线程,分别传递消息队列和消息数量。
    • 使用 join 等待两个线程完成。

示例运行

当运行这个示例时,你会看到生产者和消费者线程交替打印消息的生产和消费情况。由于生产时间和消费时间不同,你会看到它们以不同的速率处理消息。

注意事项

  • std::lock_guard 和 std::unique_lock 用于确保对共享资源的互斥访问。
  • std::condition_variable 用于线程间的同步,确保消费者线程在队列为空时不会继续执行,直到有新消息可用。

这个示例展示了如何在C++中使用消息队列和线程同步机制实现简单的生产者-消费者模型。

 

 =============================================================================

 

线程间通信是并发编程中的一个重要概念,它允许不同的线程之间交换信息或协调它们的执行。除了使用消息队列(如上述示例中的 MessageQueue 类)之外,还有其他多种方式可以实现线程间通信。以下是一些常见的线程间通信方式:

  1. 共享内存:
    • 线程可以访问共享的内存区域来交换数据。这通常需要使用同步机制(如互斥锁、信号量等)来避免数据竞争和不一致性问题。
    • 优点:通信速度快,因为数据直接在内存中访问。
    • 缺点:需要仔细管理同步,以避免死锁、优先级反转等问题。
  2. 管道(Pipes):
    • 管道是一种半双工的通信方式,它允许一个线程(写端)将数据写入管道,而另一个线程(读端)从管道中读取数据。
    • 在Unix-like系统中,管道是进程间通信的一种传统方式,但也可以在线程间使用。
    • 优点:简单且易于使用。
    • 缺点:通常是单向的,且在某些情况下可能不够灵活。
  3. 套接字(Sockets):
    • 虽然套接字主要用于网络通信,但它们也可以在同一台机器上的不同线程或进程间进行通信。
    • 使用套接字可以实现全双工的通信,并支持多种传输协议(如TCP、UDP等)。
    • 优点:适用于跨网络的通信,且提供了丰富的通信选项。
    • 缺点:相对复杂,且可能引入额外的开销。
  4. 信号量(Semaphores):
    • 信号量是一种用于线程间同步和通信的机制。它允许一个或多个线程等待某个条件成立(例如,某个资源可用)。
    • 信号量可以是二元的(用于互斥锁)或计数的(用于限制对资源的访问数量)。
    • 优点:提供了灵活的同步机制。
    • 缺点:需要谨慎管理,以避免死锁等问题。
  5. 条件变量(Condition Variables):
    • 条件变量与互斥锁一起使用,允许线程在特定条件不满足时等待,并在条件满足时被唤醒。
    • 它们通常用于实现生产者-消费者模式等线程间通信场景。
    • 优点:与互斥锁结合使用,可以确保线程安全地等待和唤醒。
    • 缺点:需要额外的同步机制来管理条件变量的状态。
  6. 事件(Events):
    • 在某些编程环境中(如Windows的线程库),事件是一种用于线程间通信的机制。
    • 一个线程可以设置一个事件的状态(例如,信号或重置),而另一个线程可以等待该事件的发生。
    • 优点:提供了一种简单的同步机制。
    • 缺点:可能受限于特定的编程环境。
  7. 消息传递(Message Passing):
    • 除了上述的基于共享内存的消息队列外,还可以使用其他形式的消息传递机制(如消息总线、分布式消息系统等)。
    • 这些机制通常允许线程或进程通过发送和接收消息来通信。
    • 优点:提供了灵活且可扩展的通信方式。
    • 缺点:可能引入额外的复杂性和开销。

在您提供的代码示例中,已经展示了使用消息队列(基于 std::queue 和 std::mutex)进行线程间通信的一种方式。这种方式属于基于共享内存的消息传递机制。根据您的需求,上述列出的其他方式也是线程间通信的有效选择,具体选择哪种方式取决于您的应用场景和需求。

 

标签:std,队列,间通信,线程,消息,Message
From: https://www.cnblogs.com/music-liang/p/18548052

相关文章

  • 多线程篇·线程相关知识
    一、线程状态线程是cpu任务调度的最小执行单位,每个线程拥有自己独立的程序计数器、虚拟机栈、本地方法栈。线程状态包括:创建、就绪、运行、阻塞、死亡。二、线程状态切换三、阻塞唤醒过程阻塞以下三个方法的调用都会使当前线程阻塞,该线程将会被放置到对该Object的请......
  • 多线程篇·线程相关知识
    一、线程状态线程是cpu任务调度的最小执行单位,每个线程拥有自己独立的程序计数器、虚拟机栈、本地方法栈。线程状态包括:创建、就绪、运行、阻塞、死亡。二、线程状态切换三、阻塞唤醒过程阻塞以下三个方法的调用都会使当前线程阻塞,该线程将会被放置到对该Object的请......
  • 线程状态切换
    Java线程控制方法的作用及其区别:start:作用:启动线程,由虚拟机自动调度执行run()方法。区别:线程处于就绪状态。run:作用:线程逻辑代码块处理,JVM调度执行。区别:线程处于运行状态。sleep:作用:让当前正在执行的线程休眠(暂停执行)。区别:不释放锁。wait:......
  • 线程状态切换
    Java线程控制方法的作用及其区别:start:作用:启动线程,由虚拟机自动调度执行run()方法。区别:线程处于就绪状态。run:作用:线程逻辑代码块处理,JVM调度执行。区别:线程处于运行状态。sleep:作用:让当前正在执行的线程休眠(暂停执行)。区别:不释放锁。wait:......
  • 实战指南:理解 ThreadLocal 原理并用于Java 多线程上下文管理
    目录一、ThreadLocal基本知识回顾分析(一)ThreadLocal原理(二)既然ThreadLocalMap的key是弱引用,GC之后key是否为null?(三)ThreadLocal中的内存泄漏问题及JDK处理方法(四)部分核心源码回顾ThreadLocal.set()方法源码详解ThreadLocalMap.get()方法详解ThreadLocal.remove()方法源......
  • python多线程和网络编程
    一、多线程1.进程、线程和并行执行学习目标:了解什么是进程、线程,了解什么是并行执行进程比作公司,线程比作员工,多线程并行执行就比作公司的不同员工在同一时间去做不同的事。总结2.多线程编程学习目标:掌握使用threading模块完成多线程编程当你想实现唱歌和跳舞一......
  • Java核心知识体系10-线程管理
    Java系列Java核心知识体系1:泛型机制详解Java核心知识体系2:注解机制详解Java核心知识体系3:异常机制详解Java核心知识体系4:AOP原理和切面应用Java核心知识体系5:反射机制详解Java核心知识体系6:集合框架详解Java核心知识体系7:线程不安全分析Java核心知识体系8:Java如何保证线程......
  • 【JavaEE初阶 — 多线程】生产消费模型 & 阻塞队列
         1.阻塞队列     (1)阻塞队列   1.概念   阻塞队列是一种特殊的队列,也遵守"先进先出"的原则;阻塞队列能是一种线程安全的数据结构,主要用来阻塞队列的插入和获取操作:当队列满了的时候,插入操作会被阻塞,直到队列有空位。当队列为空的时......
  • Java面试之有三个线程T1,T2,T3,如何保证顺序执行?
    前言本来想着给自己放松一下,刷刷博客,突然被几道面试题难倒!有三个线程T1,T2,T3,如何保证顺序执行?似乎有点模糊了,那就大概看一下面试题吧。好记性不如烂键盘***12万字的java面试题整理***有三个线程T1,T2,T3,如何保证顺序执行?在多线程中有多种方法让线程按特定顺序执行,......
  • Java面试之多线程&并发篇(3)
    前言本来想着给自己放松一下,刷刷博客,突然被几道面试题难倒!SynchronizedMap和ConcurrentHashMap有什么区别?什么是线程安全?Thread类中的yield方法有什么作用?Java线程池中submit()和execute()方法有什么区别?似乎有点模糊了,那就大概看一下面试题吧。好记性不如烂键盘***12......