大纲 C++ 并发编程 std::async 这里不再介绍 std::async 的基础使用,而是重点介绍 std::async 进阶使用的内容。std::async 支持以下三种启动策略:
启动策略 说明 std::launch::async立即执行,创建新线程执行任务。 std::launch::deferred延迟执行,直到调用 get() 或 wait() 时才在当前线程(不会创建新线程) 执行。 默认策略 由标准库实现决定采用立即执行还是延迟执行。
std::launch::async std::launch::async 启动策略会强制异步任务在新的线程中执行,而不是延迟到获取结果时才执行。同时,与该异步任务关联的 std::future 在销毁时,会确保异步任务执行完成,因此即使主线程没有主动调用 std::future 的 get() 或 wait() 函数,程序结束前仍需要等待异步任务执行完成。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 #include <chrono> #include <future> #include <iostream> #include <thread> int process (const int milliseconds) { std::cout << "process() start, current thread id " << std::this_thread::get_id () << std::endl; const std::chrono::milliseconds ms (milliseconds) ; std::this_thread::sleep_for (ms); std::cout << "process() end, current thread id " << std::this_thread::get_id () << std::endl; return 5 ; } int main () { std::cout << "main() run, thread id " << std::this_thread::get_id () << std::endl; std::future<int > result = std::async (std::launch::async, process, 5000 ); std::cout << "continue ..." << std::endl; const int num = result.get (); std::cout << "num = " << num << std::endl; std::cout << "main() end, thread id " << std::this_thread::get_id () << std::endl; return 0 ; }
程序运行的结果如下:
1 2 3 4 5 6 main() run, thread id 1 continue ... process() start, current thread id 2 process() end, current thread id 2 num = 5 main() end, thread id 1
std::launch::deferred std::thread 创建线程后,如果系统资源紧张,线程创建可能失败并抛出异常;如果异常未被妥善处理,可能进一步导致整个程序异常退出。因此,在使用 std::thread 时需要额外考虑线程创建失败的情况 。相比之下,std::async 在某些情况下可以根据运行时策略(启动策略)决定是否创建新的线程,从而避免每次调用都显式创建线程。比如,当 std::async 使用 std::launch::deferred 启动策略时,会延迟执行任务,直到调用 std::future 的 get() 或 wait() 时才在当前线程(不会创建新线程)执行任务。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 #include <chrono> #include <future> #include <iostream> #include <thread> int process (const int milliseconds) { std::cout << "process() start, current thread id " << std::this_thread::get_id () << std::endl; const std::chrono::milliseconds ms (milliseconds) ; std::this_thread::sleep_for (ms); std::cout << "process() end, current thread id " << std::this_thread::get_id () << std::endl; return 5 ; } int main () { std::cout << "main() run, thread id " << std::this_thread::get_id () << std::endl; std::future<int > result = std::async (std::launch::deferred, process, 5000 ); std::cout << "continue ..." << std::endl; const int num = result.get (); std::cout << "num = " << num << std::endl; std::cout << "main() end, thread id " << std::this_thread::get_id () << std::endl; return 0 ; }
程序运行的结果如下:
1 2 3 4 5 6 main() run, thread id 1 continue ... process() start, current thread id 1 process() end, current thread id 1 num = 5 main() end, thread id 1
自行选择启动策略 std::launch::async | std::launch::deferred 表示两种启动策略都允许使用,由标准库实现自行选择其中一种。因此,最终启动策略不是固定的 。具体来说:
如果选择 async:任务会立即异步执行,通常在新线程中执行。 如果选择 deferred:任务不会立即执行,而是在关联的 std::future 上调用 get() 或 wait() 等等待操作时才执行,并且执行线程通常就是调用等待操作的线程(不会创建新线程)。 总结
当使用 std::launch::async | std::launch::deferred 时,表示允许标准库在异步执行和延迟执行之间进行选择,最终采用哪一种启动策略由标准库实现决定,程序不能假定一定会创建新的线程。这也是 std::async 默认启动策略的行为。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 #include <chrono> #include <future> #include <iostream> #include <thread> int process (const int milliseconds) { std::cout << "process() start, current thread id " << std::this_thread::get_id () << std::endl; const std::chrono::milliseconds ms (milliseconds) ; std::this_thread::sleep_for (ms); std::cout << "process() end, current thread id " << std::this_thread::get_id () << std::endl; return 5 ; } int main () { std::cout << "main() run, thread id " << std::this_thread::get_id () << std::endl; std::future<int > result = std::async (std::launch::async | std::launch::deferred, process, 5000 ); std::cout << "continue ..." << std::endl; const int num = result.get (); std::cout << "num = " << num << std::endl; std::cout << "main() end, thread id " << std::this_thread::get_id () << std::endl; return 0 ; }
程序运行的结果如下:
1 2 3 4 5 6 main() run, thread id 1 continue ... process() start, current thread id 2 process() end, current thread id 2 num = 5 main() end, thread id 1
判断使用的启动策略 在 C++ 中,可以使用 std::future 的 wait_for() 来判断 std::async 使用的是哪种启动策略(std::launch::async 或者 std::launch::deferred)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 #include <chrono> #include <future> #include <iostream> #include <thread> int process (const int milliseconds) { std::cout << "process() start, current thread id " << std::this_thread::get_id () << std::endl; const std::chrono::milliseconds ms (milliseconds) ; std::this_thread::sleep_for (ms); std::cout << "process() end, current thread id " << std::this_thread::get_id () << std::endl; return 5 ; } int main () { std::cout << "main() run, thread id " << std::this_thread::get_id () << std::endl; std::future<int > result = std::async (std::launch::async | std::launch::deferred, process, 5000 ); const std::future_status status = result.wait_for (std::chrono::seconds (0 )); if ( status == std::future_status::deferred ) { std::cout << "thread deferred" << std::endl; const int num = result.get (); std::cout << "result = " << num << std::endl; } else { if (status == std::future_status::ready) { std::cout << "thread finish" << std::endl; const int num = result.get (); std::cout << "result = " << num << std::endl; } else { std::cout << "thread timeout" << std::endl; const int num = result.get (); std::cout << "result = " << num << std::endl; } } std::cout << "main() end, thread id " << std::this_thread::get_id () << std::endl; return 0 ; }
程序运行的结果如下:
1 2 3 4 5 6 main() run, thread id 1 thread timeout process() start, current thread id 2 process() end, current thread id 2 result = 5 main() end, thread id 1
特别注意
std::future::wait_for() 可以用于在指定时间内等待异步任务完成,并通过返回值 std::future_status 判断任务当前状态。需要注意的是,wait_for() 本身不会影响异步任务的执行,也不会取消任务,只是用于检测任务是否在指定时间内完成 。当使用 std::async(std::launch::async, ...) 创建异步任务时,即使 wait_for() 返回 timeout,表示任务仍在执行,异步线程也会继续运行。若对应的 std::future 对象生命周期结束,在析构阶段会等待异步任务执行完成后再退出程序 。因此,wait_for() 适用于超时检测、任务状态轮询等场景,但不能用于终止正在执行的异步任务。 与 std::thread 的区别 std::thread 和 std::async 都可以用于 C++ 的异步执行,但两者的设计目标不同:std::thread 更偏向于直接管理线程,而 std::async 更偏向于管理异步任务及其执行结果。
对比项 std::threadstd::async核心定位 线程管理 异步任务管理 返回值 不直接提供返回值 可以通过 std::future 获取返回值 异常处理 线程内部异常不能直接传递给创建线程 异常会保存到 std::future,获取结果时可以重新抛出异常 线程创建 通常直接创建一个新的线程 可以选择异步执行,也可以延迟执行 启动策略 创建线程后立即开始执行 支持 std::launch::async、std::launch::deferred 执行策略 生命周期管理 需要手动 join() 或 detach() 通过 std::future 管理任务结果和等待 资源不足 创建线程失败时可能抛出异常 使用 std::launch::async 执行策略时,线程资源不足同样可能导致任务启动失败 适用场景 需要精细控制线程 更关注任务执行结果
1、std::thread 更关注「线程」
std::thread 的核心是创建和管理一个线程。使用 std::thread 时,需要关注线程的生命周期,例如:
线程什么时候创建; 线程什么时候结束; 是否需要 join(); 是否需要 detach(); 多线程之间如何同步和通信。 因此,std::thread 更适合需要对线程本身进行控制的场景。
2、std::async 更关注「任务」
std::async 的核心是异步执行一个任务,并获得任务的执行结果。std::async 通常与 std::future 配合使用:提交任务 → 异步执行 → 返回 std::future → 通过 future 获取结果。 因此,如果一个函数有返回值,并且希望在后台执行完成后获取这个结果,std::async 通常比 std::thread 更方便。 3、std::async 可以控制任务的执行策略
std::async 可以指定:std::launch::async:要求异步执行任务,通常由新的线程执行。std::launch::deferred:延迟执行任务,直到需要获取结果时才执行,并且在调用 get() 或 wait() 的线程中执行。std::launch::async | std::launch::deferred:由标准库实现自行选择执行策略。std::thread 本身没有这种异步 / 延迟执行策略选择机制。4、std::async 对返回值和异常处理更加方便
std::thread 本身没有提供线程函数返回值的机制,通常需要通过:
共享变量; std::promise;条件变量; 其他线程间通信机制 来传递结果;而 std::async 天然与 std::future 配合,可以直接获取任务返回值。异常处理也类似:
std::thread 中线程函数抛出的未捕获异常,会导致程序调用 std::terminate();std::async 会将任务中的异常保存到共享状态中,在通过 std::future 获取执行结果时重新抛出异常。5、线程数量并不是越多越好
无论是 std::thread 还是使用 std::launch::async 的 std::async,都需要消耗系统线程资源。如果短时间内创建大量线程,可能导致:
创建线程失败; 系统资源消耗过大; 线程上下文切换增加; 程序性能下降。 因此,对于大量并发任务,通常不应该简单地通过不断创建 std::thread 或 std::async 来解决,而应该考虑线程池、任务队列等机制。
总结
在 C++ 中,std::thread 是对线程进行管理,std::async 是对异步任务进行管理。 如果关注的是线程本身的创建、生命周期以及线程控制,可以优先考虑使用 std::thread。 如果关注的是任务的异步执行、返回结果、异常处理以及等待任务完成,使用 std::async 通常更加方便。 从抽象层次来看,std::thread 偏向 线程级别 → 更关注手动管理线程,而 std::async 偏向 任务级别 → 更关注任务执行结果。 std::future 这里不再介绍 std::future 的基础使用,而是重点介绍 std::future 进阶使用的内容。
wait_for () 的使用 std::future::wait_for() 用于等待异步任务在指定时间内完成,它不会获取任务结果,而是阻塞当前线程一段时间,并通过返回值表示任务当前状态。返回值类型为 std::future_status,包含三种状态:ready 表示异步任务已完成,可以通过 get() 获取结果;timeout 表示等待时间结束,但任务仍未完成;deferred 表示任务采用延迟执行策略,需要调用 get() 或 wait() 时才会执行。wait_for() 适合用于超时检测、周期性检查异步任务状态等场景,常与 std::async、std::promise、std::packaged_task 等异步机制配合使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 #include <chrono> #include <future> #include <iostream> #include <thread> int process (int milliseconds) { std::cout << "process() start, current thread id " << std::this_thread::get_id () << std::endl; const std::chrono::milliseconds ms (milliseconds) ; std::this_thread::sleep_for (ms); std::cout << "process() end, current thread id " << std::this_thread::get_id () << std::endl; return 5 ; } int main () { std::cout << "main() run, thread id " << std::this_thread::get_id () << std::endl; std::future<int > result = std::async (std::launch::async, process, 5000 ); std::cout << "continue ..." << std::endl; std::future_status status = result.wait_for (std::chrono::seconds (2 )); if (status == std::future_status::timeout) { std::cout << "future timeout" << std::endl; } else if (status == std::future_status::ready) { std::cout << "future ready" << std::endl; }else if (status == std::future_status::deferred) { std::cout << "future deferred" << std::endl; } else { std::cout << "future unknow status" << std::endl; } std::cout << "main() end, thread id " << std::this_thread::get_id () << std::endl; return 0 ; }
程序运行的结果如下:
1 2 3 4 5 6 main() run, thread id 1 continue ... process() start, current thread id 2 future timeout main() end, thread id 1 process() end, current thread id 2
特别注意
std::future::wait_for() 可以用于在指定时间内等待异步任务完成,并通过返回值 std::future_status 判断任务当前状态。需要注意的是,wait_for() 本身不会影响异步任务的执行,也不会取消任务,只是用于检测任务是否在指定时间内完成 。当使用 std::async(std::launch::async, ...) 创建异步任务时,即使 wait_for() 返回 timeout,表示任务仍在执行,异步线程也会继续运行。若对应的 std::future 对象生命周期结束,在析构阶段会等待异步任务执行完成后再退出程序 。因此,wait_for() 适用于超时检测、任务状态轮询等场景,但不能用于终止正在执行的异步任务。 std::shared_future std::shared_future 的概述 概述:
std::shared_future 是 C++ 11 引入的异步结果共享机制,用于允许多个线程或多个对象共享同一个异步结果 。它与 std::future 类似,都可以获取异步任务的结果,但 std::future 通常只能由一个对象获取结果,而 std::shared_future 支持多个对象重复访问同一个结果。 特点:
异步结果共享 :多个 std::shared_future 对象可以共享同一个异步结果。支持重复获取 :可以多次调用 get() 获取相同的结果,而 std::future 的 get() 通常只能调用一次。线程间共享结果 :适合将同一个异步结果传递给多个线程。支持异常共享 :如果异步任务产生异常,多个 std::shared_future 对象调用 get() 时都可以获得该异常。支持拷贝 :std::shared_future 支持拷贝,多个对象可以共享同一个共享状态。可由 std::future 转换 :可以通过 std::future::share() 将 std::future 转换为 std::shared_future,转换后原 std::future 不再有效。常用成员函数:
get():获取异步任务的结果,可以被多次调用。wait():等待异步任务完成。wait_for():等待指定时间,并返回任务状态。wait_until():等待到指定时间点,并返回任务状态。valid():判断当前 shared_future 是否关联有效的共享状态。与 std::future 的区别:
组件 作用 std::future独占异步结果,通常只能获取一次结果 std::shared_future共享异步结果,可以被多个对象、多个线程获取
总结
std::shared_future 是一种可共享、可重复获取的异步结果对象 ,主要解决 std::future 结果只能由单个对象获取的问题。它适合需要将同一个异步结果提供给多个线程或多个消费者的场景。
std::shared_future 的使用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 #include <chrono> #include <future> #include <iostream> #include <thread> int process (const int milliseconds) { std::cout << "process() start, current thread id " << std::this_thread::get_id () << std::endl; const std::chrono::milliseconds ms (milliseconds) ; std::this_thread::sleep_for (ms); std::cout << "process() end, current thread id " << std::this_thread::get_id () << std::endl; return 5 ; } void process2 (const std::shared_future<int > &result) { std::cout << "process2() start, current thread id " << std::this_thread::get_id () << std::endl; std::cout << "result = " << result.get () << std::endl; std::cout << "process2() end, current thread id " << std::this_thread::get_id () << std::endl; } int main () { std::cout << "main() run, thread id " << std::this_thread::get_id () << std::endl; std::packaged_task<int (int ) > task (process) ; std::future<int > result = task.get_future (); std::shared_future<int > result2 (result.share()) ; std::thread t1 (std::ref(task), 5000 ) ; std::thread t2 (process2, std::ref(result2)) ; t1.join (); t2.join (); const int num = result2.get (); std::cout << "result = " << num << std::endl; std::cout << "main() end, thread id " << std::this_thread::get_id () << std::endl; return 0 ; }
程序运行的结果如下:
1 2 3 4 5 6 7 8 main() run, thread id 1 process() start, current thread id 2 process2() start, current thread id 3 process() end, current thread id 2 result = 5 process2() end, current thread id 3 result = 5 main() end, thread id 1
std::atomic 原子操作(Atomic Operation)是指在执行过程中不会被任何其他操作中断或干扰的最小、不可分割的操作单元。它要么全部执行成功,要么全部执行失败,不会出现一半执行成功一半执行失败的状态 。在多线程或多进程环境中,原子操作对于保证数据一致性至关重要,因为当多个线程同时访问和修改共享数据时,原子操作能确保某一时刻只有一个线程能完成该操作,从而避免了数据竞争和不一致问题。硬件层面,CPU 提供了如比较并交换(CAS)等指令来直接支持原子操作;软件层面,编程语言和操作系统则通过锁、信号量等机制,或提供专门的原子类型和函数,来确保操作的原子性。原子操作是构建并发程序、无锁数据结构以及各种同步机制的基础基石。
std::atomic 的概述 概述:
std::atomic 是 C++ 11 引入的原子操作机制,用于对共享数据进行线程安全的原子操作,避免多个线程同时访问共享变量时产生数据竞争。它通常用于多线程环境中的计数器、状态标志、共享变量等场景,可以保证对变量的读写操作具有原子性。 特点:
原子操作:对 std::atomic 对象的读写、修改等操作具有原子性,不会被其他线程看到中间状态。 避免数据竞争:多个线程同时操作同一个 std::atomic 对象时,可以避免因非原子访问导致的数据竞争。 线程安全:适合在多个线程之间安全地共享和修改简单类型的数据。 高效轻量:相比使用互斥锁保护简单共享变量,原子操作通常具有更低的开销。 支持多种操作:支持加载、存储、交换、比较并交换以及算术运算等原子操作。 支持内存序:可以通过 std::memory_order 指定不同的内存序,控制多线程之间的内存访问顺序。 常用成员函数:
load():原子地读取当前值。store():原子地写入新值。exchange():原子地替换当前值,并返回替换之前的值。compare_exchange_weak():比较当前值与期望值,如果相等则更新为新值;失败时允许出现伪失败。compare_exchange_strong():比较当前值与期望值,如果相等则更新为新值,不允许出现伪失败。fetch_add():原子地执行加法操作,并返回操作之前的值。fetch_sub():原子地执行减法操作,并返回操作之前的值。fetch_and():原子地执行按位与操作,并返回操作之前的值。fetch_or():原子地执行按位或操作,并返回操作之前的值。fetch_xor():原子地执行按位异或操作,并返回操作之前的值。std::atomic 的使用 C++ 存在线程安全的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 #include <iostream> #include <thread> static int g_count = 0 ;void process () { for (int i = 0 ; i < 1000000 ; ++i) { g_count++; } } int main () { std::thread t1 (process) ; std::thread t2 (process) ; t1.join (); t2.join (); std::cout << "count = " << g_count << std::endl; return 0 ; }
程序运行的结果如下:
特别注意
上面这段案例代码存在线程安全问题。g_count 是多个线程共享的普通变量,而 g_count++ 并不是原子操作,实际上包含读取、加 1 和写回三个步骤。当两个线程同时执行时,可能读取到相同的旧值,导致更新丢失,因此存在数据竞争。更严格地说,在 C++ 内存模型下,数据竞争会导致未定义行为。join() 只能保证主线程等待两个子线程执行完成,并不能解决两个子线程之间对 g_count 的并发访问问题。可以使用 std::atomic<int> 对 g_count 进行原子化,或者使用 std::mutex 对临界区进行加锁,从而保证线程安全。
使用 std::atomic 解决线程安全问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 #include <atomic> #include <iostream> #include <thread> static std::atomic<int > g_count (0 ) ;void process () { for (int i = 0 ; i < 1000000 ; ++i) { g_count++; } } int main () { std::thread t1 (process) ; std::thread t2 (process) ; t1.join (); t2.join (); std::cout << "count = " << g_count.load () << std::endl; return 0 ; }
程序运行的结果如下:
std::atomic 的注意事项 std::atomic 原子操作主要用于多线程环境下保证共享数据访问的线程安全性。对于整数类型,通常支持自增(++)、自减(--)、加法赋值(+=)、减法赋值(-=)、按位与赋值(&=)、按位或赋值(|=)以及按位异或赋值(^=)等原子运算,但具体支持的操作取决于原子类型和编译器实现,并非所有操作都适用于所有 std::atomic 类型。
std::atomic 不支持原子操作的写法(存在线程安全问题)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 #include <iostream> #include <atomic> #include <thread> static std::atomic<int > g_count (0 ) ;void process () { for (int i = 0 ; i < 1000000 ; ++i) { g_count = g_count + 1 ; } } int main () { std::thread t1 (process) ; std::thread t2 (process) ; t1.join (); t2.join (); std::cout << "count = " << g_count.load () << std::endl; return 0 ; }
程序运行的结果如下:
其他常见的互斥量 std::recursive_mutex std::recursive_mutex 的概述 定义:std::recursive_mutex 是 C++ 11 提供的递归互斥锁,也称为可重入锁。 特点:同一个线程可以多次获取同一把锁,不会因为重复加锁而导致死锁。 解锁规则:每次 lock() 都必须对应一次 unlock(),只有加锁次数归零后,锁才真正释放。 适用场景:适用于类的多个成员函数相互调用或者递归调用,并且这些函数都需要对同一资源加锁的场景。 注意:如果没有递归加锁的需求,推荐优先使用 std::mutex,因为它的语义更加简单明确。 总结
std::recursive_mutex 是支持同一线程多次获取同一把锁的互斥量,每次加锁(lock())都必须对应一次解锁(unlock())。
std::recursive_mutex 的使用 在下面的案例代码中,使用了 std::recursive_mutex,因此同一个线程可以重复获取 m_mutex,不会因为递归加锁而导致线程死锁。特别注意,如果将它换成 std::mutex m_mutex;,递归调用 process() 时,同一个线程再次加锁,就会产生线程死锁。
使用 std::recursive_mutex + std::unique_lock
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 #include <iostream> #include <mutex> class MyClass {public : void process (int count) { std::unique_lock<std::recursive_mutex> lock (m_mutex) ; std::cout << "count = " << count << std::endl; if (count > 0 ) { process (count - 1 ); } } private : std::recursive_mutex m_mutex; }; int main () { MyClass obj; obj.process (3 ); return 0 ; }
程序运行的结果如下:
1 2 3 4 count = 3 count = 2 count = 1 count = 0
std::timed_mutex std::timed_mutex 的概述 定义:std::timed_mutex 是 C++ 11 提供的带超时功能的互斥锁。 特点:除了支持普通的加锁、解锁外,还支持尝试加锁并设置等待时间。 超时机制:如果在指定时间内获取不到锁,可以放弃等待并继续执行其他逻辑,避免线程无限阻塞。 常用成员函数:try_lock_for():等待指定时间。try_lock_until():等待到指定时间点。 适用场景:适用于不希望线程长时间阻塞,需要设置加锁超时时间的场景。 注意:如果不需要超时控制,通常使用 std::mutex 即可。 std::timed_mutex 的使用 案例一:std::timed_mutex 使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 #include <iostream> #include <thread> #include <mutex> #include <chrono> class MyClass {public : void process () { if (m_mutex.try_lock_for (std::chrono::seconds (3 ))) { std::cout << "成功获取锁, 线程 ID 是 " << std::this_thread::get_id () << std::endl; std::this_thread::sleep_for (std::chrono::seconds (5 )); m_mutex.unlock (); } else { std::cout << "获取锁超时, 线程 ID 是 " << std::this_thread::get_id () << std::endl; } } private : std::timed_mutex m_mutex; }; int main () { MyClass obj; std::thread t1 (&MyClass::process, &obj) ; std::thread t2 (&MyClass::process, &obj) ; t1.join (); t2.join (); return 0 ; }
程序运行的结果如下:
1 2 成功获取锁, 线程 ID 是 2 获取锁超时, 线程 ID 是 3
案例二:std::timed_mutex + std::unique_lock 使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 #include <chrono> #include <iostream> #include <mutex> #include <thread> class MyClass {public : void process () { std::unique_lock<std::timed_mutex> lock (m_mutex, std::defer_lock) ; if (lock.try_lock_for (std::chrono::seconds (3 ))) { std::cout << "成功获取锁, 线程 ID 是 " << std::this_thread::get_id () << std::endl; std::this_thread::sleep_for (std::chrono::seconds (5 )); } else { std::cout << "获取锁超时, 线程 ID 是 " << std::this_thread::get_id () << std::endl; } } private : std::timed_mutex m_mutex; }; int main () { MyClass obj; std::thread t1 (&MyClass::process, &obj) ; std::thread t2 (&MyClass::process, &obj) ; t1.join (); t2.join (); return 0 ; }
程序运行的结果如下:
1 2 成功获取锁, 线程 ID 是 2 获取锁超时, 线程 ID 是 3
std::recursive_timed_mutex std::recursive_timed_mutex 的概述 定义:std::recursive_timed_mutex 是 C++ 11 提供的支持递归加锁和超时机制的互斥锁。 递归加锁:同一个线程可以多次获取同一把锁,不会因为重复加锁而死锁。 超时机制:支持在指定时间内尝试获取锁,超时后返回失败。 常用成员函数:try_lock_for():尝试获取锁,最多等待指定时间。try_lock_until():尝试获取锁,等待到指定时间点。 解锁规则:每次 lock() 都必须对应一次 unlock(),只有加锁次数归零后,锁才真正释放。 适用场景:既需要递归加锁,又需要超时控制的场景。 总结
std::recursive_timed_mutex = std::recursive_mutex 的递归能力 + std::timed_mutex 的超时能力。
std::recursive_timed_mutex 的使用 使用 std::recursive_timed_mutex + std::unique_lock
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 #include <chrono> #include <iostream> #include <mutex> #include <thread> class MyClass {public : void process (int count) { std::unique_lock<std::recursive_timed_mutex> lock (m_mutex, std::defer_lock) ; if (lock.try_lock_for (std::chrono::seconds (3 ))) { std::cout << "成功获取锁, 线程 ID 是 " << std::this_thread::get_id () << ", count = " << count << std::endl; std::this_thread::sleep_for (std::chrono::seconds (2 )); if (count > 0 ) { process ((count - 1 )); } } else { std::cout << "获取锁超时, 线程 ID 是 " << std::this_thread::get_id () << std::endl; } } private : std::recursive_timed_mutex m_mutex; }; int main () { MyClass obj; obj.process (3 ); return 0 ; }
程序运行的结果如下:
1 2 3 4 成功获取锁, 线程 ID 是 1, count = 3 成功获取锁, 线程 ID 是 1, count = 2 成功获取锁, 线程 ID 是 1, count = 1 成功获取锁, 线程 ID 是 1, count = 0