Как правильно остановить g_main_loop_run? - PullRequest
1 голос
/ 21 февраля 2020

Я использую GLib впервые. Итак, чтобы понять, как использовать API GLib, я написал следующую фиктивную программу:

#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;
}

Когда я запускаю программу, она не завершается sh, она застревает. Это вывод, который он печатает.

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_quit и g_main_loop_unref выполняются раньше g_main_loop_run. Как мне исправить эти проблемы?

РЕДАКТИРОВАТЬ после комментария пользователя 7860670: Спасибо пользователю 7860670 за предложение. Вот рабочий код сейчас.

#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;
}

1 Ответ

1 голос
/ 21 февраля 2020

Похоже, что executor e; может go выйти из области видимости до того, как рабочий поток начнет работать, и, поскольку на ~executor() вы отменяете l oop и объекты контекста перед ожиданием рабочего потока, их число ссылок падает. до 0, и они разрушаются до работы рабочего потока. Вы должны сделать это по-другому: дождаться окончания рабочего потока до sh и только затем отменить l oop и объекты контекста.

...