我可以从Qt中的异常处理程序中发出信号吗

Can I emit a signal from within a exception handler in Qt?

本文关键字:信号 Qt 我可以 异常处理程序      更新时间:2023-10-16

Qt文档说明:

从Qt的信号槽连接机制调用的槽抛出异常被认为是未定义的行为,除非它在槽中处理

我希望在其中一个UI对象中设置错误文本。实现这一点的一种优雅方式是信号/插槽机制。由于I可以在插槽中有异常(如果我也在插槽中处理它们),问题仍然存在:

从异常处理程序内部发出信号是否安全

一个相关的问题是连接机制:

使用排队连接更好(或更糟),还是直接连接可以

我想做这样的事情:

class myclass : public QObject
{
    Q_OBJECT
public:
    myclass() {}
signals:
    void error_message(const std::string &);
public slots:
    void do_mogrify()
    {
         try {
              // complex function that may throw
         } catch (std::exception & e) {
             emit error_message(std::string(e.what()));
         }
    }
} 

更新:

Craig Scott在他的回答中指出,当slot函数被执行时,提供给信号的自变量可能不再存在。因此,我已经将示例更改为使用std::string,如果需要,它将被复制(排队连接)。我想它也适用于参考文献。

我想强调的是,这个问题是关于线程的而不是。从线程发出信号在Qt文档和此处都有介绍。

允许在catch块内部发出信号。仔细考虑您用于信号的参数,记住客户端代码可以选择是直接连接到该信号还是通过排队连接(即,您不知道在发出信号时会使用哪个)。在您的示例中,您的信号有一个const char*参数,在catch块中,您使用异常的what()函数作为该参数。如果客户端代码通过排队连接连接到信号,则在调用插槽时,const char*自变量可能不再存在。如果要从catch块发出信号,请考虑仅使用可安全复制的参数类型(例如,通过值传递的std::string而不是const char*)。

如果您想在非GUI线程中捕获异常并在那里发出信号,然后在GUI线程中捕捉该信号以向用户显示某种错误消息,则需要排队连接(仅为一个示例使用场景)。只要您注意如上所述使用的参数类型,排队连接是可以的。就我个人而言,我认为明智的做法是假设捕获块发出的信号可以通过排队连接连接(对未来的更改更健壮,等等)。

更新:

具体来说,使用排队连接而不是直接连接是好是坏,这取决于对你来说重要的是什么。直接连接是最有效的,可以保证您的代码立即响应异常,但您要负责线程安全。直接连接实际上只是将信号发射变成一个普通的函数调用,尽管在这个过程中需要一些内务处理。排队连接将把对异常的响应延迟到未来的任意点,即使它在同一线程中响应(如果拥有插槽的对象属于同一线程,但可能会先处理其他信号,则当控制返回到当前线程的事件循环时,将处理排队响应)。

您的问题没有提到其他连接类型,但默认为Qt::AutoConnection,这意味着如果拥有插槽的对象属于同一线程,则行为将与直接相同,如果属于不同线程,则排队类似,因此这种连接类型可能会导致您的代码立即响应异常,或者可能会延迟,这意味着您再次需要自己处理线程安全问题。Qt::BlockingQueuedConnection可以保证您的代码立即响应,但对性能有潜在的负面影响,并且需要更加小心地避免死锁等。

因此,虽然您更新的问题表明您没有专门询问线程,但线程注意事项与您的问题密切相关,即哪种连接类型更好。最终,这取决于您对更好的标准。

它是安全的。从异常处理程序运行代码和从其他地方运行代码没有区别。直接连接和排队连接都一样好。