我正在尝试使用
libnm
库从一个线程调用 NetworkManager 调用,而另一个线程则负责迭代上下文并处理异步方法的回调。然而,无论我做什么,我似乎都无法摆脱这三个关键的glib
警告:
GLib-CRITICAL **: 15:55:38.490: g_main_context_push_thread_default: assertion 'acquired_context' failed
GLib-CRITICAL **: 15:55:38.490: g_main_context_pop_thread_default: assertion 'g_queue_peek_head (stack) == context' failed
libnm-CRITICAL **: 15:55:38.491: ((libnm/nm-client.c:5925)): assertion '<dropped>' failed
我做了一个最低限度可重复的例子,如下所示
#include <thread>
#include <glib.h>
#include <gio/gio.h>
#include <iostream>
#include <NetworkManager.h>
class NetworkManager
{
public:
NetworkManager()
{
std::cout << "Network manager creation" << std::endl;
GError* error;
m_client = nm_client_new(nullptr, &error);
m_context = nm_client_get_main_context(m_client);
m_thread = std::thread([this]() {
g_main_context_push_thread_default(m_context);
while (true)
{
g_main_context_iteration(m_context, true);
}
g_main_context_pop_thread_default(m_context);
});
std::this_thread::sleep_for(std::chrono::seconds(1));
nm_client_reload_connections_async(m_client, nullptr, (GAsyncReadyCallback)callback, this);
}
~NetworkManager() {};
static void callback(GObject*, GAsyncResult*, gpointer data)
{
auto nm_ptr = static_cast<NetworkManager*>(data);
bool success = nm_client_reload_connections_finish(nm_ptr->m_client, nullptr, nullptr);
std::cout << success << std::endl;
}
std::thread m_thread;
GMainContext* m_context;
NMClient* m_client;
};
int main() {
NetworkManager nm{};
int i = 0;
while(true)
{
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << i++ << std::endl;
}
}
我是否误解了主上下文线程推送和弹出的工作原理?我已经阅读了 Main Context 教程,但我仍然不完全理解如何从另一个线程正确处理主上下文。
libnm 为您提供
NMClient
。这是您在 D-Bus API 上看到的数据的客户端缓存。
该缓存本身不是线程安全的。 ...嗯,它是线程安全的,因为每次通过它的访问都必须经过关联的
GMainContext
。
您无法从多个线程访问数据。除非,您确保他们不会同时访问数据。确保这一点的建议方法是在访问之前获取 GMainContext。
通常,每个线程都有一个 GMainContext。但您也可以在线程之间传递一个 GMainContext。但话虽如此,您仍然必须在访问数据之前获取上下文。这里的“获取”是指获得独占访问权。如果你做一些不平凡的事情,你就需要提防死锁。
也许您应该只让一个线程运行 GMainContext 并访问 NMClient。然后在线程之间传递数据(例如,通过使用
g_idle_source_new()
在其他线程中附加回调)。
你会得到断言失败,因为
g_main_context_iteration()
将获取GMainContext。另一个线程无法再次获取它,并且尝试以这种方式使用 NMClient 会失败。
我建议再读一遍教程。 IMO 非常好。