如何在多线程 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
锁包裹着。
问题:这种行为的原因是什么?我能确保它不会发生吗?或者我可以捕获此错误并期望从中恢复吗?
编辑:
我试过了
- 我尝试从使用
std::thread
更改为 Glib::Threads::Thread
,但无济于事,仍然出现相同的错误。
- 阅读 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::Thread
和 std::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" 流程,这是一个更好的选择。当然也有没有固定周期的应用,那就要寻求其他的解决方案了。
我正在尝试编写一个使用 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
锁包裹着。
问题:这种行为的原因是什么?我能确保它不会发生吗?或者我可以捕获此错误并期望从中恢复吗?
编辑:
我试过了
- 我尝试从使用
std::thread
更改为Glib::Threads::Thread
,但无济于事,仍然出现相同的错误。 - 阅读 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::Thread
和std::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" 流程,这是一个更好的选择。当然也有没有固定周期的应用,那就要寻求其他的解决方案了。