Sobes.tech
Middle+

Ako skontrolovať, či došlo k výnimke v jednom z vlákien?

sobes.tech AI

Odpoveď od AI

Za kontrolu výnimky v vlákne je možné použiť niekoľko prístupov, v závislosti od toho, ako bolo vlákno vytvorené a ako sa spracovávajú jeho výsledky.

  1. Použitie std::future a std::async: Ak bolo vlákno spustené pomocou std::async, výsledkom bude objekt std::future. Volaním metód get() alebo wait() na tomto objekte spôsobí, že vyvolaná výnimka vo vlákne bude znovu vyhodená vo volajúcom vlákne.

    #include <future>
    #include <iostream>
    #include <stdexcept>
    
    void thread_function() {
        throw std::runtime_error("Chyba vo vlákne!");
    }
    
    int main() {
        std::future<void> future = std::async(std::launch::async, thread_function);
    
        try {
            future.get(); // Čaká na dokončenie a hodí výnimku, ak bola
        } catch (const std::exception& e) {
            std::cerr << "Výnimka zachytená z vlákna: " << e.what() << std::endl;
        }
    
        return 0;
    }
    
  2. Použitie std::promise a std::future: Môže byť explicitne odovzdané std::promise do vlákna alebo ho možno uchovávať mimo neho, aby vlákno mohlo uložiť výnimku.

    #include <future>
    #include <iostream>
    #include <thread>
    #include <stdexcept>
    
    void thread_function_with_promise(std::promise<void>&& promise) {
        try {
            throw std::runtime_error("Ešte jedna chyba vo vlákne!");
            promise.set_value(); // Nastaví hodnotu (ak nebola výnimka)
        } catch (...) {
            promise.set_exception(std::current_exception()); // Uloží výnimku
        }
    }
    
    int main() {
        std::promise<void> promise;
        std::future<void> future = promise.get_future();
    
        std::thread t(thread_function_with_promise, std::move(promise));
    
        try {
            future.get(); // Čaká na dokončenie a hodí uloženú výnimku
        } catch (const std::exception& e) {
            std::cerr << "Výnimka zachytená z vlákna (pomocou promise): " << e.what() << std::endl;
        }
    
        t.join(); // Čaká na dokončenie vlákna
    
        return 0;
    }
    
  3. Ručné riadenie výnimiek: Vlákno môže zachytiť výnimku vo vnútri seba a uložiť ju (napríklad v std::exception_ptr) alebo oznámiť iné vlákno o jej výskyte (napríklad cez atomickú premennú alebo mutex s podmienkovou premennou).

    #include <iostream>
    #include <thread>
    #include <atomic>
    #include <exception>
    #include <mutex>
    #include <condition_variable>
    
    std::exception_ptr global_exception_ptr = nullptr;
    std::mutex exception_mutex;
    std::condition_variable exception_cv;
    bool exception_occurred = false;
    
    void thread_function_manual() {
        try {
            throw std::logic_error("Logická chyba v vlákne!");
        } catch (...) {
            std::lock_guard<std::mutex> lock(exception_mutex);
            global_exception_ptr = std::current_exception(); // Uložiť aktuálne výnimku
            exception_occurred = true;
            exception_cv.notify_one(); // Oznámiť čakajúce vlákno
        }
    }
    
    int main() {
        std::thread t(thread_function_manual);
    
        // Hlavné vlákno môže čakať na oznámenie o výnimke
        std::unique_lock<std::mutex> lock(exception_mutex);
        exception_cv.wait(lock, []{ return exception_occurred; });
    
        if (global_exception_ptr) {
            try {
                std::rethrow_exception(global_exception_ptr); // Znovu vyhodiť uloženú výnimku
            } catch (const std::exception& e) {
                std::cerr << "Výnimka zachytená z vlákna (ručné riadenie): " << e.what() << std::endl;
            }
        }
    
        t.join();
    
        return 0;
    }
    

Voľba metódy závisí od kontextu: std::async s std::future je najpohodlnejší na získanie výsledku alebo výnimky z samostatnej úlohy, zatiaľ čo std::promise/std::future alebo ručné riadenie môžu byť užitočné v zložitejších scenároch synchronizácie.