Более-менее глубокой информации о задачах в Rust не много, поэтому, для того что бы разобраться в том, как же это работает, приходится экспериментировать “на кошках”. Посидев некоторое время за исходниками Rust, я открыл для себя много нового.
Самое неожиданное: на данный момент планировщиков задач аж два! Один старый, более функциональный, правда, на данный момент находящийся в довольно непотребном состоянии и новый, который еще не дописан. Подобная ситуация приводит к тому, что поведение задач, запущенных при помощи методов
Ну а после “оптимистичного” введения, небольшой рассказ о планировщиках Rust и том, как с ними работать на данный момент.
Старый планировщик
Если зайти на сайт Rust, то в разделе посвященном модулю Task можно найти информацию о ряде доступных типах планировщиков, заявленное поведение которых расходится с реальным:
DefaultScheduler – создается по одному OS потоку на ядро процессора.PlatformThread – все задачи выполняются в одном потоке последовательно.SingleThreaded ,ThreadPerCore ,ManualThreads(x) – создаются по одному OS потоку на задачу.ThreadPerTask – при попытке содать планировщик этого типа вылетает исключение. Загадочно, не правда ли?
Как видно, поведение планировщиков совершенно не соответствует их названиям, а задачи запускаемые на старых планировщиках игнорируют флаги
В принципе, я надеюсь что данные ошибки будут исправлены, так как работать с задачами с использованием специально настроенных планировщиков было довольно удобно:
schld.sched_mode(task::DefaultScheduler); // (2)
schld.unlinked(); // (3)
do shd.spawn { // (4)
to_do_sometnig();
}
Для того, что бы иметь возможность не только указать желаемый планировщик, но и настроить его параметры, необходимо обзавестись объектом
Новый планировщик
На данный момент, новый планировщик используется вместе с наиболее распространенным синтаксисом запуска задач, использующим функции из модуля task:
to_do_sometnig();
}
Но, в отличие от примера со старым планировщиком, задача вполне может быть запущена в
to_do_sometnig();
fail!(); // (2)
}
В этом примере, вызов функции
Тестовый код и отладочный вывод
Для тестирования я написал небольшую програмку, доступную тут, основной код которой выглядит следующим образом:
io::println(fmt!("Spawning #%u", i));
let mut shd = task::task();
shd.sched_mode(self.mode);
let timeout = self.timeout as u32;
do shd.spawn {
io::println(fmt!("Before sleeping #%u", i));
unsafe{ libc::sleep(timeout); }
io::println(fmt!("Waked up #%u", i));
}
}
Отладочный вывод получился информативным. Для планировщика по умолчанию, который де-факто является
Spawning #0
Spawning #1
Before sleeping #0
Spawning #2
Before sleeping #1
Spawning #3
Spawning #4
Spawning #5
Spawning #6
Before sleeping #2
Spawning #7
Spawning #8
Spawning #9
Spawning #10
Spawning #11
Before sleeping #7
Waked up #7
Waked up #1
Waked up #2
Waked up #0
Before sleeping #11
Before sleeping #8
Before sleeping #5
Before sleeping #10
Waked up #10
Waked up #11
Waked up #8
Waked up #5
Before sleeping #6
Before sleeping #4
Before sleeping #9
Before sleeping #3
Waked up #6
Waked up #3
Waked up #9
Waked up #4
Т.е. все задачи отправились на выполнение, но потоков всего 4, поэтому, задачи выводят сообщения о начале своей работы по 4 подряд.
Если отладочный вывод планировщика по умолчанию заставляет хоть немного задуматься, что в случае с планировщиком
Spawning #0
Spawning #1
Spawning #2
Spawning #3
Spawning #4
Spawning #5
Spawning #6
Before sleeping #0
Spawning #7
Spawning #8
Spawning #9
Spawning #10
Spawning #11
Waked up #0
Before sleeping #7
Waked up #7
Before sleeping #8
Waked up #8
Before sleeping #1
Waked up #1
Before sleeping #10
Waked up #10
Before sleeping #9
Waked up #9
Before sleeping #3
Waked up #3
Before sleeping #2
Waked up #2
Before sleeping #11
Waked up #11
Before sleeping #6
Waked up #6
Before sleeping #5
Waked up #5
Before sleeping #4
Waked up #4
У всех остальных планировщиков вывод будет приблизительно одинаковый, с различиями лишь в том, какая из задач первой отправилась на выполнение:
Spawning #0
Spawning #1
Before sleeping #0
Spawning #2
Before sleeping #1
Spawning #3
Before sleeping #2
Spawning #4
Before sleeping #3
Spawning #5
Before sleeping #4
Spawning #6
Before sleeping #5
Spawning #7
Before sleeping #6
Spawning #8
Before sleeping #7
Spawning #9
Before sleeping #8
Spawning #10
Before sleeping #9
Spawning #11
Before sleeping #10
Before sleeping #11
Waked up #0
Waked up #3
Waked up #2
Waked up #1
Waked up #4
Waked up #5
Waked up #6
Waked up #7
Waked up #8
Waked up #9
Waked up #11
Waked up #10