Вероятно, есть несколько способов избежать блокировки основного цикла, но лучший из них, вероятно, зависит от того, какое событие приведет к разрыву цикла while
.
Если вам действительно нужно «опросить» какое-то условие, самым простым подходом, я думаю, будет цикл тайм-аута:
function myPollingFunc(arg1, arg2) {
if (the_event_occured) {
// Here is where you will invoke whatever it is you want to do
// when the event occurs, then destroy the source since the
// condition has been met.
this.classMethod(arg1, arg2);
return GLib.SOURCE_REMOVE;
}
// If the condition was not met, you can wait until the next loop and
// check again.
return GLib.SOURCE_CONTINUE;
}
// Probably you'll want to store this ID (maybe as a property on your class),
// so you can destroy the source if the DBus service is destroyed before the
// event you're waiting for occurs.
let sourceId;
sourceId = GLib.timeout_add_seconds(
// Generally this priority is fine, but you may choose another lower one
// if that makes sense
GLib.PRIORITY_DEFAULT,
// The timeout interval of your loop. As described in the linked article,
// second-based loops tend to be a better choice in terms of performance,
// however note that at least one interval will pass before the callback
// is invoked.
1,
// Your callback function. Since you're probably creating the source from
// a class method and intend on modifying some internal state of your class
// you can bind the function to the class scope, making it's internal state
// and other methods available to your callback.
//
// Function.bind() also allows you to prepend arguments to the callback, if
// that's necessary or a better choice. As noted above, you'll probably want
// to store the ID, which is especially important if you bind the callback to
// `this`.
//
// The reason is that the callback will hold a reference to the object it's
// bound to (`this` === `Foo`), and the source will hold the callback in the
// loop. So if you lose track of the source and the ability to destroy it, the
// object will never be garbage collected.
myPollingFunc.bind(this, arg1, arg2)
);
Вы можете сделать то же самое с незанятым источником, который ожидает, пока не будет ожидающих событий с более высоким приоритетом, а не фиксированный тайм-аут. Уловка с незанятыми источниками заключается в том, что если нет других ожидающих событий, ваш обратный вызов будет многократно вызываться почти так же быстро, как цикл while
, и вы, вероятно, истощите основной цикл (например, затрудните для других событий получить ногу в дверях).
С другой стороны, может быть более простой способ сделать это, если вам на самом деле не нужно опрашивать условие, но вы ожидаете отправки сигнала или изменения свойства GObject:
...
_runLoop () {
// Waiting for some object to emit a signal or change a property
let handlerId = someObject.connect('some-signal', () => {
someObject.disconnect(handlerId);
this.classMethod(arg1, arg2);
});
let propId = someGObject.connect('notify::some-prop', () => {
if (someGObject.some_prop === 'expected_value') {
someGObject.disconnect(propId);
this.classMethod(arg1, arg2);
}
});
}
...
Не зная, какого типа событие или условие вы ожидаете, трудно дать лучший совет по лучшему решению, но, возможно, это поможет. В общем, я бы сказал, что если вы думаете, что хотите зациклиться на каком-то условии, вам лучше проверить это условие в существующем основном цикле GLib, чем создавать свой собственный подцикл.
ИЗМЕНИТЬ
С большим контекстом кажется, что вы так или иначе будете полагаться на внешний процесс. В этом случае я бы настоятельно рекомендовал избегать низкоуровневых функций порождения в GLib и вместо этого использовать GSubprocess.
Это гораздо более безопасный выбор для языковых привязок более высокого уровня, а также включает вспомогательные функции, написанные на C, которые вы, скорее всего, все равно перепишете:
onProcExited(proc, result) {
try {
proc.wait_check_finish(proc, result);
} catch (e) {
logError(e);
}
}
onLineRead(stdout, result) {
try {
let line = stdout.read_line_finish_utf8(result)[0];
// %null generally means end of stream
if (line !== null) {
// Here you can do whatever processing on the line
// you need to do, and this will be non-blocking as
// all the I/O was done in a thread.
someFunc(line);
// Now you can request the next line
stdout.read_line_async(null, onLineRead.bind(this));
}
} catch (e) {
logError(e);
}
}
startFunc() {
this._proc = new Gio.Subprocess({
argv: ['proc_name', '--switch', 'arg', 'etc'],
flags: Gio.SubprocessFlags.STDOUT_PIPE
});
this._proc.init(null);
// Get the stdout pipe and wrap it in a buffered stream
// with some useful helpers
let stdout = new Gio.DataInputStream({
base_stream: this._proc.get_stdout_pipe()
});
// This function will spawn dedicated a thread, reading and buffering
// bytes until it finds a line ending and then invoke onLineRead() in
// in the main thread.
stdout.read_line_async(
GLib.PRIORITY_DEFAULT,
null // Cancellable, if you want it
onLineRead.bind(this)
);
// Check the process completion
this._proc.wait_check_async(null, onProcExited.bind(this));
}
Написание рекурсивного цикла чтения, подобного этому, довольно прямолинейно, и функция GTask (*_async()
/*_finish()
) позаботится о планировании обратных вызовов в основном цикле для вас. Весь ввод-вывод выполняется в выделенном потоке, поэтому вся работа по обработке вывода не блокируется.
Когда вы в конечном итоге отбросите ссылку на this._proc
, вы можете быть уверены, что все каналы и ресурсы очищены должным образом, избегайте оборванных файловых дескрипторов, зомби-процессов и так далее. Если вам нужно выйти из процесса раньше, вы всегда можете позвонить Gio.Subprocess.force_exit()
. Сам цикл чтения будет содержать ссылку на оболочку канала stdout, поэтому вы можете просто оставить его делать свое дело.
person
andy.holmes
schedule
29.01.2020
class Foo
службой DBus или оболочкой для клиента DBus? - person andy.holmes   schedule 28.01.2020