跟我学C++中级篇——std::shared_lock

一、读写锁

有过多语言编程的开发人员,往往在别的语言中(如Java等)看到过读写锁,它特别适合于在写少读多的情况下。一般在互联网的应用场景中,这种读非常多,写很少的场景非常普遍,所以读写锁其实应用非常多。
在C++中,原来没有这种所谓读写锁之说,只有轻量级锁和重量级锁。虽然可以通过这些锁来模拟实现读写锁,但毕竟不如原生库中带着好用。STL的发展就是要与时俱进,也要看到实际的应用需求,所以在C++14中推出了std::shared_lock。
std::shared_lock是一个通用的共享互斥体的封装,它允许延迟锁定、定时锁定和锁所有权的转移。其中使用的共享互斥体类似std::unique_lock中使用的排它性互斥体,有对比就好理解。因为它的锁是可共享的,所以它就实现了读锁的处理。写锁自然可以使用排它型锁std::unique_lock 或std::lock_gurad。

二、std::shared_lock

std::shared_lock的原理其实比较简单,先看一下其定义:

  template<typename _Mutex>
    class shared_lock
    {
   
    public:
      typedef _Mutex mutex_type;

      // Shared locking

      shared_lock() noexcept : _M_pm(nullptr), _M_owns(false) {
    }

      explicit
      shared_lock(mutex_type& __m)
      : _M_pm(std::__addressof(__m)), _M_owns(true)
      {
    __m.lock_shared(); }

      shared_lock(mutex_type& __m, defer_lock_t) noexcept
      : _M_pm(std::__addressof(__m)), _M_owns(false) {
    }

      shared_lock(mutex_type& __m, try_to_lock_t)
      : _M_pm(std::__addressof(__m)), _M_owns(__m.try_lock_shared()) {
    }

      shared_lock(mutex_type& __m, adopt_lock_t)
      : _M_pm(std::__addressof(__m)), _M_owns(true) {
    }

      template<typename _Clock, typename _Duration>
	shared_lock(mutex_type& __m,
		    const chrono
<think>嗯,用户问的是如何给运行中的线程发送数据。首先,我得回忆一下线程的基本概念。线程是操作系统调度的最小单位,每个线程有自己的执行路径。不过,线程之间共享进程的内存空间,所以理论上可以通过共享变量来传递数据。但直接这样做可能会有线程安全问题,比如竞争条件或者数据不一致的问题。那用户可能想知道的是如何安全有效地在线程运行中传递数据,而不仅仅是启动时传递参数。 接下来,我需要考虑不同的编程语言可能有不同的机制。比如在Python中,线程模块和queue模块常被用来处理线程间通信。而Java中可能有不同的方式,比如使用BlockingQueue或者Handler等。用户没有指定语言,可能需要给出通用的方法或者以常见语言为例。 然后,用户可能的应用场景是什么?比如,是否在开发一个需要实时处理任务的系统,或者是一个多线程的服务器,需要将接收到的数据分发给工作线程处理。这种情况下,可能需要一个生产者-消费者模型,使用队列来传递数据。 还要考虑线程间的同步问题。直接修改变量可能导致竞态条件,所以需要锁机制,比如互斥锁。或者使用线程安全的数据结构,如队列,这样生产者线程可以将数据放入队列,消费者线程从中取出处理。这样既安全又高效。 另外,是否有其他机制?比如事件驱动的方式,或者使用条件变量来通知线程有新数据到达。比如在Python中,可以使用queue.Queue,而在Java中可以使用BlockingQueue。或者在某些情况下,使用管道(Pipe)或者信号(Signal),不过信号处理可能比较复杂,且不是所有语言都支持。 然后,用户可能的困惑点在于如何实时发送数据到已经运行的线程,而不是在启动时传递参数。这时候,线程通常会有一个循环,不断地检查是否有新数据到达,比如从队列中获取。如果没有数据,可能会阻塞等待,这样可以节省CPU资源。 还需要提到可能的陷阱,比如死锁,或者线程没有正确响应数据的情况。比如,如果消费者线程处理数据的速度跟不上生产者,队列可能会积压,导致内存问题。或者线程没有正确终止,导致程序无法退出。 总结下来,回答应该包括几种常见方法:共享数据结构加锁、使用线程安全的队列、事件或条件变量,以及特定语言的机制如Python的Queue或Java的BlockingQueue。同时,给出示例代码可能更有帮助,比如Python中使用队列的例子,说明生产者放入数据,消费者线程处理。此外,强调线程安全和同步的重要性,避免数据竞争。 另外,需要判断用户的技术水平。问题比较基础,可能用户是刚开始学习多线程编程,需要简明扼要的解释,避免过于深入复杂的细节。但也要覆盖关键点,确保方法正确有效。 可能还需要比较不同方法的优缺点。比如共享变量需要手动加锁,容易出错,而队列更安全。或者使用消息传递模型,如Actor模型,但可能在某些语言中不原生支持。 最后,确保回答结构清晰,分点列出方法,每种方法的实现方式和注意事项,可能附上简单例子,帮助用户理解。</think>在运行中的线程之间传递数据,通常需要依赖线程间通信(Inter-Thread Communication, ITC)机制。由于线程共享进程内存空间,但直接操作共享数据可能引发竞态条件,因此需要通过同步或安全的数据结构来实现。以下是几种常见方法: --- ### **1. 共享变量 + 同步锁** - **原理**:通过全局变量共享数据,使用锁(如互斥锁)保护临界区。 - **适用场景**:简单数据交换,但需手动管理同步。 - **示例(Python):** ```python import threading shared_data = [] lock = threading.Lock() def worker(): while True: with lock: if shared_data: data = shared_data.pop(0) print(f"Processing: {data}") # 启动线程 thread = threading.Thread(target=worker) thread.start() # 主线程发送数据 with lock: shared_data.append("New Data") ``` --- ### **2. 线程安全队列(推荐)** - **原理**:使用线程安全的队列(如 `queue.Queue`)自动处理同步问题,生产者放入数据,消费者取出处理。 - **优点**:避免手动锁管理,天然支持生产者-消费者模型。 - **示例(Python):** ```python import threading import queue q = queue.Queue() def worker(): while True: data = q.get() # 阻塞等待数据 print(f"Processing: {data}") q.task_done() thread = threading.Thread(target=worker) thread.start() # 发送数据到队列 q.put("Data 1") q.put("Data 2") ``` --- ### **3. 事件或条件变量** - **原理**:通过 `threading.Event` 或 `threading.Condition` 通知线程有新数据到达。 - **适用场景**:需要线程在等待数据时休眠,减少CPU占用。 - **示例(Python条件变量):** ```python import threading data_available = threading.Condition() shared_data = [] def worker(): with data_available: while not shared_data: data_available.wait() # 释放锁并等待通知 data = shared_data.pop(0) print(f"Processing: {data}") thread = threading.Thread(target=worker) thread.start() # 发送数据并通知 with data_available: shared_data.append("New Data") data_available.notify() ``` --- ### **4. 回调函数(特定框架)** - **原理**:某些框架(如GUI库)允许通过回调函数向线程发送信号。 - **示例**:在异步编程中,主线程通过回调触发子线程处理逻辑。 --- ### **注意事项** 1. **线程安全**:直接操作共享变量必须加锁,否则会导致数据损坏。 2. **资源释放**:确保线程能正常退出(如设置退出标志)。 3. **避免死锁**:锁的获取顺序需一致,防止相互等待。 4. **队列容量**:无界队列可能导致内存溢出,可设置 `maxsize` 限制。 --- ### **不同语言的实现差异** - **Java**:使用 `BlockingQueue` 或 `wait()/notify()`。 - **C++**:通过 `std::mutex` 和 `std::condition_variable` 实现。 - **JavaScript(Node.js)**:主线程通过 `worker_threads` 的 `postMessage` 通信。 --- 选择具体方法时需权衡开发复杂度、性能需求及语言特性。通常优先使用线程安全队列,既简洁又安全。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值