Выход Валы не возвращается

У меня возникли проблемы с написанием асинхронной функции в Vala.
Я понимаю, что обратный вызов функции должен быть где-то зарегистрирован, чтобы выполнение продолжалось после операторов yield, поэтому я добавляю его в основной цикл GLib для периодического вызова. используя Idle.add. Вот мой фиктивный код:

async void loop_func()
{
    var id = Idle.add(loop_func.callback);
    message("Added idle %u",id);

    yield;
    message("yielded successfully 1");

    yield;
    message("yielded successfully 2");

    for (var i = 0; i < 20; i++)
    {
        message("%d",i);
        yield;
    }

    message("finished");
}

int main()
{
    var loop = new GLib.MainLoop();
    loop_func.begin(() => {loop.quit();});
    loop.run();
    return 0;
}

Несмотря на это, код после второго оператора yield никогда не выполняется. Это видно из вывода:

$ ./async   
** Message: 20:07:24.932: async.vala:4: Added idle 1
** Message: 20:07:24.932: async.vala:7: yielded successfully 1

А потом зависает.
Что я здесь упускаю?


person Albert Tomanek    schedule 07.09.2020    source источник
comment
Мне не ясно, что должно произойти, когда асинхронная функция имеет несколько операторов yield;. Вы взяли этот пример откуда-то, что может дать больше контекста?   -  person ptomato    schedule 10.09.2020
comment
Я хотел бы выполнять некоторые дорогостоящие операции в цикле for, но возвращать управление ЦП после каждой итерации, чтобы предотвратить зависание потока. Каждый раз, когда я уступаю после итерации цикла, я хотел бы, чтобы выполнение продолжалось оттуда, когда ЦП снова освобождается. Я нашел способ сделать это сейчас; Спасибо за вашу помощь!   -  person Albert Tomanek    schedule 10.09.2020
comment
Целью yield является организация асинхронного обратного вызова, поэтому, когда трудоемкая операция завершается в отдельной функции, поток возвращается к выходу, GIO имеет множество асинхронных функций, например. yield file.mount_mountable (MountMountFlags.NONE);   -  person AlThomas    schedule 10.09.2020
comment
Да, есть много примеров асинхронных функций с несколькими yield some_other_async_function();, но я не нашел ни одной с несколькими yield;.   -  person ptomato    schedule 11.09.2020


Ответы (1)


Хотя GLib неоднократно вызывает функцию бездействия, пока не сообщит ей об остановке (возвращая false), .callback асинхронной функции, похоже, перестает повторяться сразу после вызова.

Это можно преодолеть, добавляя обратный вызов каждый раз перед оператором yield:

async void loop_func()
{
    for (var i = 0; i < 20; i++)
    {
        Idle.add(loop_func.callback);
        yield;
    }
}

Альтернативный (и, возможно, более эффективный) способ сделать это — игнорировать значение, которое возвращает обратный вызов асинхронной функции:

async void loop_func()
{
    var id = Idle.add(() => {loop_func.callback(); return Source.CONTINUE;});

    for (var i = 0; i < 20; i++)
    {
        yield;
    }

    Source.remove(id);
}
person Community    schedule 10.09.2020
comment
MainLoop является абстракцией над GMainContext, который представляет собой цикл событий GLib и может быть создан для каждого потока или более, что позволяет использовать несколько циклов событий, если вы пытаетесь добавить управление потоком к трудоемким операциям, но хотите поддерживать отдельный реагирующий поток, например. для пользовательского ввода, тогда можно взглянуть на developer.gnome.org/gnome-devel-demos/stable/ для вдохновения - person AlThomas; 10.09.2020