You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

跨线程处理NetworkManager的GMainContext时遇GLib断言错误求助

问题:多线程使用libnm时GLib关键警告处理

我尝试在一个线程调用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 '' 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的线程推入和弹出机制?已阅读Main Context教程,但仍未完全理解如何在另一个线程中正确处理main context。


原因分析与修复方案

错误根源

  1. 上下文所有权未获取:nm_client_get_main_context返回的上下文默认属于创建NMClient的主线程,直接在子线程调用g_main_context_push_thread_default会触发acquired_context断言失败——GLib要求必须先获取上下文的独占访问权,才能将其设为线程默认上下文。
  2. 清理代码无法执行:子线程中的g_main_context_pop_thread_default被死循环while(true)阻塞,永远不会执行,导致上下文栈状态不一致,触发第二个断言。
  3. libnm断言失败:异步操作的回调需要在归属正确的上下文线程执行,上下文归属错误会引发libnm内部断言失败。

修复后的代码

#include <thread>
#include <glib.h>
#include <gio/gio.h>
#include <iostream>
#include <NetworkManager.h>
#include <atomic>

class NetworkManager
{
public:
    NetworkManager() : m_running(true)
    {
        std::cout << "Network manager creation" << std::endl;
        GError* error = nullptr;
        m_client = nm_client_new(nullptr, &error);
        if (!m_client) {
            std::cerr << "Failed to create NMClient: " << error->message << std::endl;
            g_error_free(error);
            return;
        }
        m_context = nm_client_get_main_context(m_client);
        m_thread = std::thread([this]() {
            // 先获取上下文独占访问权,这是push线程默认的前提
            if (!g_main_context_acquire(m_context)) {
                std::cerr << "Failed to acquire main context" << std::endl;
                return;
            }
            // 将上下文设为当前线程的默认上下文
            g_main_context_push_thread_default(m_context);
            
            // 使用GMainLoop替代手动迭代,更符合GLib规范且易控制退出
            m_loop = g_main_loop_new(m_context, FALSE);
            g_main_loop_run(m_loop);
            
            // 退出循环后依次清理资源
            g_main_loop_unref(m_loop);
            g_main_context_pop_thread_default(m_context);
            g_main_context_release(m_context);
        });
        // 等待子线程完成上下文初始化
        std::this_thread::sleep_for(std::chrono::seconds(1));
        nm_client_reload_connections_async(m_client, nullptr, (GAsyncReadyCallback)callback, this);
    }
    
    ~NetworkManager() {
        if (m_loop) {
            g_main_loop_quit(m_loop);
        }
        if (m_thread.joinable()) {
            m_thread.join();
        }
        if (m_client) {
            g_object_unref(m_client);
        }
    };
    
    static void callback(GObject*, GAsyncResult* res, gpointer data)
    {
        auto nm_ptr = static_cast<NetworkManager*>(data);
        GError* error = nullptr;
        bool success = nm_client_reload_connections_finish(nm_ptr->m_client, res, &error);
        if (error) {
            std::cerr << "Reload failed: " << error->message << std::endl;
            g_error_free(error);
        } else {
            std::cout << "Reload success: " << success << std::endl;
        }
    }

private:
    std::thread m_thread;
    GMainContext* m_context;
    GMainLoop* m_loop;
    NMClient* m_client;
    std::atomic<bool> m_running;
};


int main() {
    NetworkManager nm{};
    int i = 0;
    while(true)
    {
        std::this_thread::sleep_for(std::chrono::seconds(1));
        std::cout << i++ << std::endl;
    }
}

关键说明

  • g_main_context_acquire:必须先调用此函数获取上下文的独占访问权,否则无法将其设置为线程默认上下文,这是解决第一个断言的核心。
  • 使用GMainLoop:手动死循环迭代上下文难以正确退出,g_main_loop_run会自动处理事件循环,退出时只需调用g_main_loop_quit,保证后续清理代码(pop、release)能正常执行。
  • 资源清理:析构函数中停止主循环、等待线程结束、释放NMClient,避免资源泄漏和线程残留。
  • 完善错误处理:添加GError的检查与释放,便于排查操作失败原因。

内容的提问来源于stack exchange,提问作者joulsen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 23:11:17