Nếu bạn đang sử dụng C ++ 11, sau đó std::future
có thể thực hiện chính xác những gì bạn đang tìm kiếm: nó có thể Automagically bẫy ngoại lệ mà làm cho nó lên trên cùng của các sợi nhân, và vượt qua chúng thông qua các chủ đề cha mẹ tại thời điểm đó std::future::get
là gọi là. (Phía sau, điều này xảy ra chính xác như trong câu trả lời của @AnthonyWilliams; nó vừa được triển khai cho bạn.)
Mặt trái của nó là không có cách tiêu chuẩn nào để "ngừng quan tâm đến" a std::future
; thậm chí trình hủy của nó sẽ đơn giản chặn cho đến khi tác vụ được hoàn thành. [EDIT, 2017: Các hành vi chặn-destructor là một misfeature chỉ của pseudo-tương lai trở về từ std::async
, mà bạn không bao giờ nên sử dụng anyway. Hợp đồng tương lai bình thường không chặn trong trình hủy của chúng. Nhưng bạn vẫn không thể "hủy" nhiệm vụ nếu đang sử dụng std::future
: (các) nhiệm vụ thực hiện lời hứa sẽ tiếp tục chạy ở hậu trường ngay cả khi không ai lắng nghe câu trả lời nữa.] Đây là một ví dụ đồ chơi có thể làm rõ điều tôi nghĩa là:
#include <atomic>
#include <chrono>
#include <exception>
#include <future>
#include <thread>
#include <vector>
#include <stdio.h>
bool is_prime(int n)
{
if (n == 1010) {
puts("is_prime(1010) throws an exception");
throw std::logic_error("1010");
}
/* We actually want this loop to run slowly, for demonstration purposes. */
std::this_thread::sleep_for(std::chrono::milliseconds(100));
for (int i=2; i < n; ++i) { if (n % i == 0) return false; }
return (n >= 2);
}
int worker()
{
static std::atomic<int> hundreds(0);
const int start = 100 * hundreds++;
const int end = start + 100;
int sum = 0;
for (int i=start; i < end; ++i) {
if (is_prime(i)) { printf("%d is prime\n", i); sum += i; }
}
return sum;
}
int spawn_workers(int N)
{
std::vector<std::future<int>> waitables;
for (int i=0; i < N; ++i) {
std::future<int> f = std::async(std::launch::async, worker);
waitables.emplace_back(std::move(f));
}
int sum = 0;
for (std::future<int> &f : waitables) {
sum += f.get(); /* may throw an exception */
}
return sum;
/* But watch out! When f.get() throws an exception, we still need
* to unwind the stack, which means destructing "waitables" and each
* of its elements. The destructor of each std::future will block
* as if calling this->wait(). So in fact this may not do what you
* really want. */
}
int main()
{
try {
int sum = spawn_workers(100);
printf("sum is %d\n", sum);
} catch (std::exception &e) {
/* This line will be printed after all the prime-number output. */
printf("Caught %s\n", e.what());
}
}
Tôi vừa cố gắng viết một ví dụ giống công việc bằng cách sử dụng std::thread
và std::exception_ptr
, nhưng có gì đó không ổn với std::exception_ptr
(sử dụng libc ++) nên tôi vẫn chưa làm cho nó thực sự hoạt động. :(
[EDIT, 2017:
int main() {
std::exception_ptr e;
std::thread t1([&e](){
try {
::operator new(-1);
} catch (...) {
e = std::current_exception();
}
});
t1.join();
try {
std::rethrow_exception(e);
} catch (const std::bad_alloc&) {
puts("Success!");
}
}
Tôi không biết mình đã làm gì sai trong năm 2013, nhưng tôi chắc chắn đó là lỗi của tôi.]