std::future
用途:std::future 用于获取异步操作的结果。它充当一个占位符,等待异步操作的结果可用。
使用场景:通常由 std::async、std::promise::get_future() 或 std::packaged_task::get_future() 返回。
只可移动:std::future 是 只可移动 的,意味着它不能被复制,只能被移动。这限制了结果的获取只能由一个线程完成。
所有权:拥有 std::future 的线程可以调用 .get() 来获取结果,一旦调用了 .get(),结果就会被消耗掉(此时 future 失效)。
常用方法:
get():等待并返回结果(如果异步操作中出现异常,会抛出异常)。
valid():检查 future 是否包含有效的结果。
wait():阻塞当前线程,直到结果可用。
wait_for()、wait_until():等待指定的时间段或直到某个时间点。
std::shared_future
用途:std::shared_future 允许多个线程访问异步操作的结果。它是 std::future 的共享版本。
使用场景:可以通过 std::future::share() 从 std::future 创建 std::shared_future,这样多个线程可以共享同一个 shared_future 的副本。
可复制:与 std::future 不同,std::shared_future 是 可复制 的,意味着多个线程可以各自持有该 shared_future 的副本。
多次访问:多个线程可以调用 .get() 来获取结果,每次调用都会返回相同的结果(不会像 std::future 那样在第一次获取后失效)。
常用方法:
get():可以被多次调用,每次返回相同的异步操作结果。
valid()、wait()、wait_for()、wait_until():与 std::future 的方法相同。
std::future示例:
#include <iostream>
#include <future>
#include <chrono>
int calculateSquare(int x) {
std::this_thread::sleep_for(std::chrono::seconds(2)); // 模拟耗时操作
return x * x;
}
int main() {
// 使用 std::async 异步执行 calculateSquare 函数
std::future<int> result = std::async(std::launch::async, calculateSquare, 5);
// 主线程可以继续做其他工作
std::cout << "Doing some work while waiting for the result...\n";
// 获取异步结果
int square = result.get(); // 阻塞,直到结果可用
std::cout << "Square of 5 is: " << square << std::endl;
return 0;
}
std::shared_future 示例
#include <iostream>
#include <future>
#include <thread>
#include <chrono>
int calculateSquare(int x) {
std::this_thread::sleep_for(std::chrono::seconds(2)); // 模拟耗时操作
return x * x;
}
void printResult(std::shared_future<int> sharedResult) {
// 获取共享的异步结果
int square = sharedResult.get();
std::cout << "Result in thread: " << square << std::endl;
}
int main() {
// 使用 std::async 异步执行 calculateSquare 函数
std::future<int> result = std::async(std::launch::async, calculateSquare, 5);
// 将 std::future 转换为 std::shared_future
std::shared_future<int> sharedResult = result.share();
// 主线程可以继续做其他工作
std::cout << "Doing some work while waiting for the result...\n";
// 启动多个线程共享异步结果
std::thread t1(printResult, sharedResult);
std::thread t2(printResult, sharedResult);
// 主线程也可以获取结果
std::cout << "Result in main: " << sharedResult.get() << std::endl;
// 等待线程结束
t1.join();
t2.join();
return 0;
}
问题一
为什么需要std::future呢,直接写一个全局变量保存线程生成的值,这样是否可以?
直接使用全局变量保存线程生成的值虽然在某些情况下可以工作,但与 std::future 相比,这种方法有以下几个问题和不足:
1.线程同步和竞争条件问题
2. 结果的生命周期管理
3. 避免轮询和主动等待
4. 简化的错误处理
5. 可扩展性与线程安全
使用全局变量(不推荐):
#include <iostream>
#include <thread>
#include <atomic>
std::atomic<bool> result_ready(false); // 标志位
int result;
void calculate() {
result = 42;
result_ready = true; // 标记结果已准备好
}
int main() {
std::thread t(calculate);
// 主线程轮询等待结果
while (!result_ready) {
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
std::cout << "Result: " << result << std::endl;
t.join();
return 0;
}
这个方法不仅需要使用 std::atomic 来避免竞争条件,还需要轮询,效率较低。
std::promise
std::promise 是一个写入端,用于在线程中设置异步操作的结果。
用途:std::promise 的主要作用是提供一种手段,将某个结果或状态从一个线程传递到另一个线程。
设置值:可以通过 set_value() 来设置结果,或者通过 set_exception() 来传递异常。
获取 std::future:std::promise 可以通过 get_future() 方法生成一个与之关联的 std::future,用于读取结果。
std::promise 和 std::future 之间的关系
std::promise 是生产者,用于在线程中生成并提供数据。
std::future 是消费者,用于在线程中等待并接收数据。
它们的组合为多线程间的通信提供了有效的方式,一个线程可以通过 std::promise 设置值或异常,另一个线程则通过 std::future 获取该值或处理异常。
具体例子
假设我们有两个线程,一个线程计算结果并通过 std::promise 传递给另一个线程:
#include <iostream>
#include <future>
#include <thread>
void calculateSquare(std::promise<int>& prom, int x) {
// 模拟耗时操作
std::this_thread::sleep_for(std::chrono::seconds(2));
int result = x * x;
prom.set_value(result); // 将结果传递给 promise
}
int main() {
std::promise<int> prom; // 创建 promise
std::future<int> fut = prom.get_future(); // 获取与 promise 关联的 future
std::thread t(calculateSquare, std::ref(prom), 10); // 在线程中执行任务
std::cout << "Waiting for result...\n";
// 主线程等待结果
int square = fut.get(); // 阻塞直到结果可用
std::cout << "Square of 10 is: " << square << std::endl;
t.join(); // 等待线程结束
return 0;
}
异常处理
std::promise 还可以通过 set_exception() 向 std::future 传递异常。当调用 get() 时,异常会被捕获并抛出。
#include <iostream>
#include <future>
#include <thread>
#include <exception>
void throwException(std::promise<int>& prom) {
try {
throw std::runtime_error("An error occurred!");
} catch (...) {
prom.set_exception(std::current_exception()); // 将异常传递给 promise
}
}
int main() {
std::promise<int> prom;
std::future<int> fut = prom.get_future();
std::thread t(throwException, std::ref(prom)); // 在线程中执行任务
try {
int result = fut.get(); // 获取结果,但由于异常会抛出异常
} catch (const std::exception& e) {
std::cout << "Caught exception: " << e.what() << std::endl;
}
t.join();
return 0;
}
future只能和std::async配合使用吗?
std::future 的确常常与 std::async 一起使用,但它不仅限于这种组合。std::future 也可以与 std::promise 配合使用,或与其他线程创建方法(如 std::thread)结合使用。
1. std::future 和 std::async
#include <iostream>
#include <future>
#include <chrono>
int calculateSquare(int x) {
std::this_thread::sleep_for(std::chrono::seconds(2)); // 模拟耗时计算
return x * x;
}
int main() {
std::future<int> fut = std::async(std::launch::async, calculateSquare, 10);
std::cout << "Waiting for the result..." << std::endl;
int result = fut.get(); // 阻塞等待计算结果
std::cout << "Result: " << result << std::endl;
return 0;
}
std::future 和 std::promise
#include <iostream>
#include <thread>
#include <future>
void calculateSquare(std::promise<int> prom, int x) {
// 模拟耗时操作
std::this_thread::sleep_for(std::chrono::seconds(1));
prom.set_value(x * x); // 设置结果
}
int main() {
std::promise<int> prom; // 创建 promise
std::future<int> fut = prom.get_future(); // 从 promise 获取 future
std::thread t(calculateSquare, std::move(prom), 10); // 传递 promise
int result = fut.get(); // 等待结果
std::cout << "Result: " << result << std::endl;
t.join(); // 确保线程结束
return 0;
}
上面为什么不用std::ref, 而是用std::move?
在使用 std::promise 和 std::future 时,选择使用 std::move 而不是 std::ref 的原因主要与 std::promise 的所有权和生命周期管理有关
std::move(prom) 后,在子线程函数内设置了值并且子线程先结束了,后面主线程在调用获取值会不会应该prom对象不存在了而出问题呢?
1.std::move 的效果
使用 std::move(prom) 之后,prom 对象的所有权转移给了子线程,意味着:
在子线程中可以安全地调用 prom.set_value(...) 或 prom.set_exception(...) 来设置结果。
主线程中的 prom 变量将不再有效,因为它的状态已经被移动。
2. 子线程结束
在子线程中设置值后,子线程可以安全结束。此时,如果主线程调用 fut.get(),并不会因为 prom 对象已不存在而导致问题,因为 std::future 维护了对 std::promise 的内部状态。
3. std::future 的状态管理
std::future 实际上是持有了 std::promise 的状态(即结果或异常),而不是 std::promise 本身。因此,即使 std::promise 对象在子线程中被移动,std::future 仍然能够安全地获取设置的值。
4. 代码示例
以下是一个完整的示例,展示了在子线程中设置值后,主线程如何安全地调用 fut.get():
#include <iostream>
#include <thread>
#include <future>
#include <chrono>
void calculateSquare(std::promise<int> prom, int x) {
// 模拟耗时计算
std::this_thread::sleep_for(std::chrono::seconds(1));
prom.set_value(x * x); // 设置结果
// 子线程结束
}
int main() {
std::promise<int> prom; // 创建 promise
std::future<int> fut = prom.get_future(); // 从 promise 获取 future
std::thread t(calculateSquare, std::move(prom), 10); // 转移所有权
// 主线程可以继续做其他工作
std::cout << "Main thread is doing other work..." << std::endl;
t.join(); // 等待子线程结束
int result = fut.get(); // 阻塞等待结果
std::cout << "Result: " << result << std::endl;
return 0;
}
std::promise 和 std::future 之间的状态管理和数据获取是 C++11 标准库线程支持的一部分,背后的实现设计非常巧妙。尽管标准库的具体实现可能会有所不同(如在不同的编译器或平台上),下面是一般的实现原理和逻辑。
1. 内部数据结构
std::promise 和 std::future 通常有一个共享的内部状态。这个状态可能是一个结构体或类,包含以下内容:
结果值:用来存储异步计算的结果。
异常:用来存储在计算过程中发生的异常(如果有的话)。
状态标志:指示结果是否已设置,可能的状态包括未设置、已设置(成功)、已设置(异常)。
2. 关联机制
当你创建一个 std::promise 对象时,它通常会有一个与之关联的内部状态,这个状态通过指针或引用的形式与 std::future 关联。
当你调用 std::promise::set_value() 或 std::promise::set_exception() 时,实际是设置了这个共享状态。
std::future 在创建时会接收一个指向这个共享状态的引用或指针,这样它可以访问相同的数据。
3. 实现步骤
具体实现步骤可能如下:
创建状态:
在 std::promise 的构造函数中创建一个状态对象,并保存其指针。
设置值或异常:
当调用 set_value() 或 set_exception() 时,std::promise 会更新这个共享状态。
获取值:
当调用 std::future::get() 时,std::future 会检查共享状态:
如果状态表示值已设置,则返回该值。
如果状态表示异常,则抛出异常。
如果值尚未设置,get() 将阻塞,直到值被设置。
4. 线程安全
为了保证线程安全,std::promise 和 std::future 的实现通常会使用互斥锁(mutex)来保护共享状态。这确保了多个线程在访问同一个状态时不会出现数据竞争。
5. 示例伪代码
以下是一个简化的伪代码示例,展示了这种内部实现的基本逻辑:
class SharedState {
public:
std::optional<int> result; // 存储结果
std::exception_ptr exception; // 存储异常
std::mutex mtx; // 互斥锁
std::condition_variable cv; // 条件变量
bool ready = false; // 状态标志
// 设置值
void set_value(int val) {
std::lock_guard<std::mutex> lock(mtx);
result = val;
ready = true;
cv.notify_all(); // 通知等待的线程
}
// 设置异常
void set_exception(std::exception_ptr e) {
std::lock_guard<std::mutex> lock(mtx);
exception = e;
ready = true;
cv.notify_all(); // 通知等待的线程
}
};
class promise {
std::shared_ptr<SharedState> state; // 共享状态
public:
promise() : state(std::make_shared<SharedState>()) {}
future get_future() {
return future(state); // 返回与状态关联的 future
}
void set_value(int val) {
state->set_value(val);
}
void set_exception(std::exception_ptr e) {
state->set_exception(e);
}
};
class future {
std::shared_ptr<SharedState> state; // 共享状态
public:
future(std::shared_ptr<SharedState> s) : state(s) {}
int get() {
std::unique_lock<std::mutex> lock(state->mtx);
state->cv.wait(lock, [this]() { return state->ready; }); // 等待值准备好
if (state->exception) {
std::rethrow_exception(state->exception); // 抛出异常
}
return *state->result; // 返回结果
}
};