如何在多线程 gtkmm 应用程序中对抗 "Fatal IO error 11 (Resource temporarily unavailable) on X server"?

How to combat "Fatal IO error 11 (Resource temporarily unavailable) on X server" in multithreaded gtkmm application?

我正在尝试编写一个使用 C++11 多线程的 gtkmm 应用程序。但是,我将 运行 保留为 Fatal IO error 11 (Resource temporarily unavailable) on X server 错误。

我的 window 上有几个 Gtk::Image 对象。每个都有自己的 Gtk::EventBox,而且我必须定期更改图像。为此,我创建了一个 class 来保存特定块的事件框,并且它具有删除先前图像、生成新图像并将其放置在那里的功能。

这是一段代码:

while (lock_flag.test_and_set()) {} // std::atomic_flag lock
// ...
std::cerr << 1;
eventBox->foreach(
    [eb = this->eventBox](Gtk::Widget& w)
    {
        eb->Gtk::Container::remove(w);
    }
);
std::cerr << 2;
eventBox->add(*im);
std::cerr << 3;
eventBox->show_all_children();
std::cerr << 4;
// ...
lock_flag.clear();

当出现 error 11 时,一些数字不会打印到 std::cerr,但每次出现问题的地方都不一样(我最近观察到它在 12 后和 123 后崩溃)。因此我得出结论,某处正在使用的资源不是图像,而是 eventBox。但是在程序初始化之后,在这个函数之外的任何地方都无法访问它,并且这个函数被 std::atomic_flag 锁包裹着。

问题:这种行为的原因是什么?我能确保它不会发生吗?或者我可以捕获此错误并期望从中恢复吗?

编辑:

我试过了

  1. 我尝试从使用 std::thread 更改为 Glib::Threads::Thread,但无济于事,仍然出现相同的错误。
  2. 阅读 this i have attempted to add GDK_SYNCHRONIZE to environment, this has generated [xcb] Unknown request in queue while dequeuing/[xcb] Most likely this is a multi-threaded client and XInitThreads has not been called error. This lead me to this post 之后,我尝试在启动新线程之前调用 XInitThreads()(通过 Glib::Threads::Threadstd::thread),但这没有任何作用积极的;除了有一次侥幸线程实际上执行了整个功能(在屏幕上显示“4”),但仍然设法死于相同的 error 11 消息。

GTK 不是线程安全的。 You can use a global lock to access GTK from threads, but the best practice is to only invoke GTK functions from the main thread. You can use Glib::signal_idle().connect and Glib::MainContext::invoke() 为此。

最后就是我解决所有问题的方法。

通过 Phillip 提供的 link 我发现了 Glib::signal_timeout(),这使我能够完全重新思考我的代码中的并行概念。

这是我必须开始的:

if(running) return;
running = true;

static bool firstTime = true;
if(firstTime)
{
    XInitThreads();
    firstTime=false;
}

std::function<void()> f = [this] () -> void
{
    while(running)
    {
        this->takeStep();
        std::this_thread::sleep_for(std::chrono::milliseconds(300));
    }
};

std::thread(f).detach();

这很容易改写为:

if(running) return;
running = true;

runningHandle = Glib::signal_timeout().connect( sigc::mem_fun(*this, &ArtificialIntelligence::takeStep), 300 );

然后我只需要将 runningHandle.disconnect(); 添加到我的暂停功能,一切都开始美妙地工作。事实上GUI响应速度已经提升了。

因此,如果其他人正在尝试执行 "take an action, then sleep" 流程,这是一个更好的选择。当然也有没有固定周期的应用,那就要寻求其他的解决方案了。