【问题标题】:How to combat "Fatal IO error 11 (Resource temporarily unavailable) on X server" in multithreaded gtkmm application?如何在多线程 gtkmm 应用程序中解决“X 服务器上的致命 IO 错误 11(资源暂时不可用)”?
【发布时间】:2015-07-12 10:52:14
【问题描述】:

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

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

这是一段代码:

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 后,我尝试将GDK_SYNCHRONIZE 添加到环境中,这会产生[xcb] Unknown request in queue while dequeuing/[xcb] Most likely this is a multi-threaded client and XInitThreads has not been called 错误。这将我带到this post,之后我尝试在启动新线程之前调用XInitThreads()(通过Glib::Threads::Threadstd::thread),但这没有任何积极作用;除了一次侥幸,线程实际上已经执行了整个函数(在屏幕上显示“4”),但仍然设法以相同的error 11 消息终止。

【问题讨论】:

    标签: multithreading c++11 gtkmm


    【解决方案1】:

    GTK 不是线程安全的。 You can use a global lock to access GTK from threads,但最好的做法是只从主线程调用 GTK 函数。为此,您可以使用 Glib::signal_idle().connectGlib::MainContext::invoke()

    【讨论】:

      【解决方案2】:

      最后,我是如何解决所有问题的。

      通过Phillip 提供的链接,我发现了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 响应的速度已经提高了。

      因此,如果其他人试图执行“采取行动,然后休眠”的过程,这是一个更好的选择。当然也有没有固定周期的应用,然后应该寻求其他的解决方案。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-04-11
        • 1970-01-01
        • 2010-11-06
        相关资源
        最近更新 更多