【问题标题】:How to stop g_main_loop_run properly?如何正确停止 g_main_loop_run?
【发布时间】:2020-02-21 19:40:17
【问题描述】:

我是第一次使用 GLib。因此,为了了解如何使用 GLib API,我编写了以下虚拟程序。

#include<glib.h>

#include<iostream>
#include<thread>
#include<chrono>

using namespace std;
using namespace std::chrono_literals;

class executor
{
private:
        GMainLoop* main_loop;
        GMainContext* worker_context;
        thread worker_thread;

        void worker_loop()
        {
                g_main_context_push_thread_default(worker_context);
                cout << "Starting main loop" << endl;
                g_main_loop_run(main_loop);
                cout << "Finished main loop" << endl;
                g_main_context_pop_thread_default(worker_context);
        }

public:
        executor()
        {
                worker_context = g_main_context_new();
                main_loop = g_main_loop_new(worker_context, false);
                worker_thread = thread(&executor::worker_loop, this);
        }

        ~executor()
        {
                g_main_loop_quit(main_loop);
                cout << "Stopped main loop from running" << endl;
                g_main_loop_unref(main_loop);
                g_main_context_unref(worker_context);
                if (worker_thread.joinable())
                {
                        worker_thread.join();
                }
        }

        void queue_callback(int (*callback)(void))
        {
                GSource* idle_source = g_idle_source_new();
                g_source_set_callback(idle_source, (GSourceFunc)callback, NULL, NULL);
                g_source_attach(idle_source, worker_context);
                g_source_unref(idle_source);
        }
};

int func1()
{
        cout << "func1 started" << endl;
        this_thread::sleep_for(5s);
        cout << "func1 finished waiting" << endl;
        return 0;
}

int func2()
{
        cout << "func2 started" << endl;
        this_thread::sleep_for(1s);
        cout << "func2 finished waiting" << endl;
        return 0;
}

int main()
{
        executor e;
        e.queue_callback(func1);
        e.queue_callback(func2);
        return 0;
}

当我运行程序时,它没有完成执行,它卡住了。这是它打印的输出。

Stopped main loop from running
Starting main loop
func1 started
func1 finished waiting
func2 started
func2 finished waiting

有时它会因以下错误而失败

Stopped main loop from running
Starting main loop

(process:16343): GLib-CRITICAL **: 10:58:54.405: g_main_loop_run: assertion 'g_atomic_int_get (&loop->ref_count) > 0' failed
Finished main loop

我的猜测是这两个问题都在发生,因为 g_main_loop_quitg_main_loop_unrefg_main_loop_run 之前运行。如何解决这些问题?

在 user7860670 发表评论后编辑: 感谢 user7860670 的建议。这是现在的工作代码。

#include<glib.h>

#include<iostream>
#include<thread>
#include<chrono>

using namespace std;
using namespace std::chrono_literals;

class executor
{
private:
        GMainLoop* main_loop;
        GMainContext* worker_context;
        thread worker_thread;

        void worker_loop()
        {
                g_main_context_push_thread_default(worker_context);
                cout << "Starting main loop" << endl;
                g_main_loop_run(main_loop);
                cout << "Finished main loop" << endl;
                g_main_context_pop_thread_default(worker_context);
        }

public:
        executor()
        {
                worker_context = g_main_context_new();
                main_loop = g_main_loop_new(worker_context, false);
                worker_thread = thread(&executor::worker_loop, this);
        }

        ~executor()
        {
                cout << "Stopping main loop" << endl;
                GSource* idle_source = g_idle_source_new();
                g_source_set_callback(idle_source, (GSourceFunc)g_main_loop_quit, main_loop, NULL);
                g_source_attach(idle_source, worker_context);
                g_source_unref(idle_source);

                if (worker_thread.joinable())
                {
                        worker_thread.join();
                }
                cout << "Removing references to main loop and context" << endl;

                g_main_loop_unref(main_loop);
                g_main_context_unref(worker_context);
        }

        void queue_callback(int (*callback)(void))
        {
                GSource* idle_source = g_idle_source_new();
                g_source_set_callback(idle_source, (GSourceFunc)callback, NULL, NULL);
                g_source_attach(idle_source, worker_context);
                g_source_unref(idle_source);
        }
};

int func1()
{
        cout << "func1 started" << endl;
        this_thread::sleep_for(5s);
        cout << "func1 finished waiting" << endl;
        return 0;
}

int func2()
{
        cout << "func2 started" << endl;
        this_thread::sleep_for(1s);
        cout << "func2 finished waiting" << endl;
        return 0;
}

int main()
{
        executor e;
        e.queue_callback(func1);
        e.queue_callback(func2);
        return 0;
}

【问题讨论】:

    标签: c++ glib


    【解决方案1】:

    看起来executor e; 可能会在工作线程开始工作之前超出范围,因为在~executor() 你在等待工作线程之前取消循环和上下文对象,它们的引用计数下降到 0,它们是在工作线程工作之前被破坏。您应该以其他方式执行此操作:等待工作线程完成,然后才取消引用循环和上下文对象。

    【讨论】:

    • 您好,感谢您的快速回复。根据您的建议,我对上面的代码进行了更改。我认为这将解决断言。但是,程序仍然没有停止执行,因为我认为g_main_loop_quit 仍然发生在g_main_loop_run 之前。我无法在线程加入后移动g_main_loop_quit,因为g_main_loop_run 是一个阻塞调用,所以加入永远不会返回,g_main_loop_quit 永远不会执行,线程永远不会停止。
    • @jyotesh 如果您希望工作线程仅在执行func1func2 后退出,那么您应该在它们之后将g_main_loop_quit 调用加入队列,而不是直接调用它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-23
    • 2019-08-09
    • 2021-06-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多