1. Vấn đề nó giải quyết
Coroutine type dùng thật phải phối hợp cooperative cancellation, lưu exception, sở hữu frame lifetime và hiểu frame allocation xảy ra ở đâu. Nó làm một giả định quan trọng trở nên rõ và có thể kiểm tra.
2. Kiến thức cần có
Task promise, stop token, RAII, exception và ownership frame.
Bạn cần biết biên dịch chương trình ngắn và đọc output.
3. Ý tưởng cốt lõi
Frame là hồ sơ công việc được quản lý: stop token đánh dấu cancellation, promise giữ failure, wrapper sở hữu destroy hồ sơ đã allocate đúng một lần. Hãy đọc unhandled_exception như lời hứa chính xác; runtime precondition vẫn do lập trình viên chịu trách nhiệm.
4. Cú pháp tối thiểu
void unhandled_exception() { error = std::current_exception(); }5. Cách nó hoạt động
Chương trình đưa vào dạng nhỏ nhất cần thiết của
unhandled_exception.Nó áp dụng tính năng lên dữ liệu cố định và giữ owner cần thiết trong scope.
Nó in một kết quả để đối chiếu trực tiếp với source.
6. Lỗi thường gặp
Destroy frame đang chạy, nuốt exception đã lưu, capture reference chết hoặc nghĩ cancellation giải phóng frame ngay đều phá tính đúng.
Hãy kiểm tra cả header, mức hỗ trợ thư viện C++20, lifetime và kiểu được suy luận.
7. Khi nào nên dùng
Nên dùng khi bạn đang implement hoặc audit ownership protocol của coroutine abstraction.
Tránh dùng khi library task đã kiểm thử cung cấp sẵn semantics cần thiết.
8. Ví dụ đơn giản
Task đếm allocation của promise, lưu exception bằng unhandled_exception, quan sát stop request và destroy frame theo RAII. Tệp .cpp không nhận input nên kết quả dễ lặp lại.
Mã mẫu hoàn chỉnh
Tệp mã nguồn
cpp20/49_coroutine_cancellation_exception_lifetime/main.cpp
// Day 49: Coroutine Cancellation, Exceptions, Lifetime, and Allocation
#include <coroutine>
#include <cstddef>
#include <exception>
#include <iostream>
#include <new>
#include <stdexcept>
#include <stop_token>
#include <utility>
int live_frames = 0;
struct Task {
struct promise_type;
using Handle = std::coroutine_handle<promise_type>;
struct promise_type {
int result{};
std::exception_ptr error{};
static void* operator new(std::size_t size) {
++live_frames;
return ::operator new(size);
}
static void operator delete(void* pointer, std::size_t size) noexcept {
--live_frames;
::operator delete(pointer, size);
}
Task get_return_object() { return Task{Handle::from_promise(*this)}; }
std::suspend_never initial_suspend() noexcept { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
void return_value(int value) noexcept { result = value; }
void unhandled_exception() { error = std::current_exception(); }
};
Handle handle{};
explicit Task(Handle value) : handle{value} {}
Task(Task&& other) noexcept : handle{std::exchange(other.handle, {})} {}
Task(const Task&) = delete;
~Task() { if (handle) handle.destroy(); }
int value() const {
if (handle.promise().error) std::rethrow_exception(handle.promise().error);
return handle.promise().result;
}
};
Task work(std::stop_token token, bool fail) {
if (token.stop_requested()) co_return -1;
if (fail) throw std::runtime_error("task failed");
co_return 1;
}
int main() {
{
std::stop_source source;
source.request_stop();
Task cancelled = work(source.get_token(), false);
Task failed = work({}, true);
std::cout << "cancelled result = " << cancelled.value() << '\n';
try { (void)failed.value(); }
catch (const std::exception& error) { std::cout << error.what() << '\n'; }
std::cout << "live frames in scope = " << live_frames << '\n';
}
std::cout << "live frames after scope = " << live_frames << '\n';
}
9. Điều cần nhớ
unhandled_exceptiondiễn đạt ý tưởng C++20 trung tâm của ngày này.Ví dụ cô lập một hành vi bằng dữ liệu cố định.
Compiler check không thay thế suy luận về lifetime và runtime.
Ưu tiên interface nhỏ nhất nói đúng yêu cầu thật.
10. Câu hỏi tự kiểm tra
Dễ — Nhiệm vụ chính của
unhandled_exceptiontrong ví dụ tối thiểu là gì?Trung bình — Exception bị throw được lưu ở đâu trước khi caller rethrow?
Khó — Vì sao request cancellation không xóa yêu cầu phải có đúng một lần
destroy()frame sau đó?