【问题标题】:throwing exception from thread is not giving expected results从线程抛出异常没有给出预期的结果
【发布时间】:2018-05-31 01:34:01
【问题描述】:

在下面的代码 sn-p 中,我试图在重新抛出相同但无法实现相同后捕获异常。我不确定出了什么问题,因为我已经通过 current_exception() 保存了当前的 teptr 状态。线程在连续循环中运行,因此一旦其值达到更大的 2,则执行 catch 块并且控制超出循环,但仍然如预期的那样,我无法在第一次尝试时到达另一个 catch 块。

#include <boost/thread.hpp>
#include <boost/thread/scoped_thread.hpp>
#include <boost/chrono.hpp>
#include <iostream>
#include <boost/exception/all.hpp>
#include <exception>
using namespace std;
boost::exception_ptr teptr;

class myexception : public exception
{
    virtual const char* what() const throw()
    {
        return "My exception happened";
    }
} myex;

class ABC
{
public:
    void start();

};

void ABC::start()
{
    int i = 0;
    cout << "running the thread" << std::endl;

    while (1)
    {
        try
        {
            std::cout << "value of " << i << '\n';

            if (i > 2)
            {
                throw  boost::enable_current_exception(myex);
            }
            i++;
        }
        catch (exception& e)
        {
            cout << "actual exception is" << e.what() << '\n';
            teptr = boost::current_exception();
            break;
            //throw myex;
        }
    }
}

int main()
{
    ABC abc;
    boost::thread thread_point;

    while (1)
    {
        boost::thread thread_point;
        thread_point = boost::thread(&ABC::start, abc);

        if (teptr) {

            try {
                boost::rethrow_exception(teptr);
            }
            catch (const std::exception &ex)
            {
                std::cerr << "Thread exited with exception: " << ex.what() << "\n";
                exit(0);
            }
        }
    }
}

【问题讨论】:

  • 在分配给teptr 之前,这里没有什么可以阻止if (teptr) 运行。你的意思是在某个地方做一个join 吗?
  • 那里不需要加入。
  • 是什么让你这么想?
  • join 阻塞,需要等待线程完成。

标签: c++ multithreading exception boost


【解决方案1】:

您的程序在不同步的情况下同时从多个线程访问变量teptr(以及myex)。该行为未定义。

更糟糕的是,您正在跟踪 thread_point 并创建许多未加入的线程。您实际上是在运行无限线程,共享相同的全局数据。

我想你真的在寻找futures - 它允许你从任何地方返回一个值或异常。所有异常处理魔法都为您完成:

Live On Coliru

#include <thread>
#include <future>
#include <iostream>
#include <sstream>

struct ABC {
    int task(int until) {
        for (int i = 0; i<10; ++i) {
            if (i > until) {
                std::ostringstream oss;
                oss << "My exception happened in thread " << std::this_thread::get_id() << " at i=" << i;
                throw std::runtime_error(oss.str());
            }
        }

        return 42;
    }
};

int main() {
    for (int runs = 0; runs < 10; ++runs) {
        ABC abc;
        std::future<int> result = std::async(&ABC::task, &abc, rand()%20);

        try {
            std::cout << "Task returned " << result.get() << "\n";
        } catch (const std::exception &ex) {
            std::cout << "Task exited with exception: " << ex.what() << "\n";
            std::cerr << "Thread exited with exception: " << ex.what() << "\n";
        }
    }
}

打印(例如):

Task returned Task exited with exception: My exception happened in thread 140288972076800 at i=4
Thread exited with exception: My exception happened in thread 140288972076800 at i=4
Task returned Task exited with exception: My exception happened in thread 140288972076800 at i=7
Thread exited with exception: My exception happened in thread 140288972076800 at i=7
Task returned 42
Task returned 42
Task returned 42
Task returned 42
Task returned Task exited with exception: My exception happened in thread 140288972076800 at i=7
Thread exited with exception: My exception happened in thread 140288972076800 at i=7
Task returned 42
Task returned 42
Task returned Task exited with exception: My exception happened in thread 140288972076800 at i=2
Thread exited with exception: My exception happened in thread 140288972076800 at i=2

【讨论】:

  • 如果你坚持使用Boost,这里是使用Boost的c++03版本Live On Coliru
  • 感谢这个解决方案。我在下面的链接上更新了我的实现。 coliru.stacked-crooked.com/a/21d556a378ccf6eb
  • 通常在线程内我需要捕获第 3 方库引发的异常,并再次需要从 catch 块重新抛出相同的异常,以便可以通过外部接口传播相同的异常。请看看这个并提出建议。这是抛出 'boost::exception_detail::clone_impl<:unknown_exception>' coliru.stacked-crooked.com/a/21d556a378ccf6eb 的实例后调用的错误 :.terminate
  • 嗨@sehe,一个最优雅的答案。我又学会了。我已经留下了我的答案,所以可以看到错误,谢谢。
【解决方案2】:

更新我的回答有缺陷且有错误,请参阅sehe的评论。

我不确定您的最终目标是什么,但要弄清楚如何处理线程中的抛出。是的,您可以通过 Boost Exception 解决编译器无法在线程之间抛出的问题。

#include <boost/thread.hpp>
#include <boost/thread/scoped_thread.hpp>
#include <boost/chrono.hpp>
#include <iostream>
#include <boost/exception/all.hpp>
#include <exception>

boost::exception_ptr teptr;
class myexception: public std::exception
{
    virtual const char* what() const throw()
    {
        return "My exception happened";
    }
} myex;


class ABCc
{
public:
    void start();
};

void ABCc::start()
{  
    int i=0;
    std::cout<<"running the thread"<<std::endl;

    while (1)
    {
        try
        {
            std::cout << "value of "<<i << '\n';

            if(i>2)
            {
                throw  boost::enable_current_exception(myex);
            }
            i++;
        }
        catch (std::exception& e)
        {
            std::cout << "actual exception is"<<e.what() << '\n';
            teptr=boost::current_exception();
            break;
            // where were you going here???????
            //throw myex;
        } 
    }
}

int main()
{
    ABCc abc;
    boost::thread thread_point;
    thread_point = boost::thread(&ABCc::start,abc);
    while(1)
    {
        if (teptr) {
            try {
                boost::rethrow_exception(teptr);
            }
            catch(const std::exception &ex) {
                std::cerr << "Thread may have exited; exception thrown: " << ex.what() << "\n";
                break;
            }
        } 
    } 
    std::cout << "exception should have been caught" << std::endl;
    return 0;
}   

请注意,您不必在 main 中投掷/接球。您通过在循环中添加 boost::thread 来创建多个线程,这是您的意图吗?

【讨论】:

  • 这段代码仍然有竞争条件。数据竞争实际上是 Undefined Behaviour - while 循环完全有可能是无限的并且永远不会检测到 teptr 的变化(为什么会这样?它不是 volatile 并且没有内存障碍。编译器 知道单次加载后的值,不需要生成任何其他加载)。
猜你喜欢
  • 2020-05-29
  • 1970-01-01
  • 2019-06-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-01-09
  • 2017-01-12
  • 2021-01-06
相关资源
最近更新 更多