c++运行多个线程并在线程抛出时在主函数中返回

问题描述 投票:0回答:1
#include <iostream>
#include <thread>
#include <vector>
#include <mutex>
#include <stdexcept>


class MyClass {
public:

    void run() {
        // do some work
    }
    struct Data {
        double x{0.0};
        int y{0};
    } data;
};

void Thread_function(int i, MyClass myclass, std::exception_ptr &exc, std::mutex &mtx)
{
    try {
        // Modify some of the struct parameters
        auto dataCopy = myclass.data;
        dataCopy.x = i + 1;
        dataCopy.y = (i + 1) * 0.5;
        // run the simulation with changed parameters
        myclass.data = dataCopy;
        myclass.run();
    } 
    catch (const std::exception &exception)
    {
        // lock capturedException
        std::lock_guard<std::mutex> lock(mtx);
        exc = std::current_exception();
    }
}

int main() {

    const int num_threads = 5;
    std::vector<std::thread> threads;
    std::exception_ptr capturedException;
    std::mutex mtx;
    MyClass myClass;

    // create threads
    for (int i = 0; i < num_threads; ++i) 
    {
        threads.emplace_back(
            Thread_function, i, myClass, std::ref(capturedException), std::ref(mtx));
    }

    // wait for all threads to finish
    for (auto& t : threads) 
    {
        t.join();
    }

    // main thread rethrows exception, if any
    if (capturedException)
    {
        throw capturedException;
        return 1;
    }

    return 0;
}

在主函数中创建了几个线程,这些线程在

run
函数中执行一些计算。在线程函数中,我使用
try/catch
块来捕获异常,并使用
std::lock_guard<std::mutex> for synchronization
,因为
exc
是所有线程之间的共享变量(通过引用传递)。 在主函数中,我等待所有线程加入,然后从线程中重新抛出异常。

但是,在 main 中,一旦线程抛出异常,我想

return 1
。无需等待其他线程。目前,
m̀ain
等待所有线程完成,尽管其中一个线程可能已经抛出异常。 我怎样才能解决这个问题? C++ 库中有类可以顺利处理这个问题吗?

c++ multithreading exception
1个回答
0
投票

让我为您画一个替代代码的示例:

#include <iostream>
#include <thread>
#include <mutex>
#include <stdexcept>
#include <condition_variable>
#include <cassert>

class MyClass {
public:

    void run() {
        // do some work
    }
    struct Data {
        double x{0.0};
        int y{0};
    } data;
};

void Thread_function(int i, MyClass myclass)
{
    // if (i==3) throw std::runtime_error("boo!");

    // Modify some of the struct parameters
    auto dataCopy = myclass.data;
    dataCopy.x = i + 1;
    dataCopy.y = (i + 1) * 0.5;
    // run the simulation with changed parameters
    myclass.data = dataCopy;
    myclass.run();
}

int main() {

    const int num_threads = 5;
    MyClass myClass;

    struct Tracking
    {
        std::mutex mutex;
        std::condition_variable condition;
        unsigned int counter=0;
        std::exception_ptr capturedException;
    } tracking;

    // create threads
    for (int i = 0; i < num_threads; ++i)
    {
        {
            std::lock_guard<decltype(tracking.mutex)> lock(tracking.mutex);
            tracking.counter++;
        }
        std::thread([&tracking, i](){
            try
            {
                Thread_function(i, MyClass());
            }
            catch(const std::exception &exception)
            {
                std::lock_guard<decltype(tracking.mutex)> lock(tracking.mutex);
                std::cout << "thread " << i << " exception\n";
                if (!tracking.capturedException)
                {
                    tracking.capturedException=std::current_exception();
                }
            }
            {
                std::lock_guard<decltype(tracking.mutex)> lock(tracking.mutex);
                tracking.counter--;
                std::cout << "thread " << i << " exit\n";
            }
            tracking.condition.notify_all();
        }).detach();
    }

    // wait for all threads to finish, or an exception to appear
    std::unique_lock<decltype(tracking.mutex)> lock(tracking.mutex);
    tracking.condition.wait(lock, [&tracking]() { return tracking.counter==0 || tracking.capturedException; });

    // main thread rethrows exception, if any
    if (tracking.capturedException)
    {
        try
        {
             std::rethrow_exception(tracking.capturedException);
        }
        catch(const std::exception& exception)
        {
            std::cerr << exception.what() << "\n";
            return EXIT_FAILURE;
        }
    }

    assert(tracking.counter==0);
    return 0;
}

我基本上坚持你的结构,但让我指出一些变化。

创建线程时,我在

Thread_Function
周围放置了一个包装器,用于处理与主线程的通信。主线程计算它创建的线程数量,而包装器代码会在线程退出时减少计数。条件变量用于告诉主线程某些内容发生了变化。

主线程等待计数达到 0,或出现异常。

另外,与您的代码不同,我只是在主线程上打印异常。没有必要把它重新扔在那里。

© www.soinside.com 2019 - 2024. All rights reserved.