Статьи

Планирование задач на Windows Phone 7

В приложении существует множество задач, которые иногда следует запланировать для последующего выполнения. Обычно подход, который могут использовать некоторые разработчики, заключается в том, чтобы отслеживать внутреннее время — сохранить значение времени в данный момент, а затем установить таймер, который будет считать до определенного числа, а затем выполнить действие. Как правило, это правдоподобное решение, но в конце концов это будет дополнительный уровень, который должен быть интегрирован с существующим кодом и может вызвать проблемы, когда дело доходит до внесения некоторых изменений в существующие блоки, которые могут нарушить механизм выполнения.

Вместо этого вы можете использовать встроенный механизм планирования — включенный в Microsoft.Phone.Reactive . Класс Scheduler позволяет вам отложить выполнение практически всего, что вам нужно. Вы можете напрямую работать с планировщиком или через IScheduler , который получит экземпляр потока из планировщика.

Давайте посмотрим на конкретный пример. Допустим, у меня есть метод, который я хочу выполнить через 2 секунды после того, как я начал другое действие. Прежде всего мне нужно решить, хочу ли я запустить метод в текущем (UI) или вторичном (фоновом) потоке.

Запуск чего-либо в потоке пользовательского интерфейса — не лучшая идея, если вам не нужно взаимодействовать с различными компонентами пользовательского интерфейса, такими как элементы управления. Но вы можете сделать это.

Прежде всего, вы должны создать экземпляр IScheduler :

IScheduler scheduler = Scheduler.NewThread;

Обратите внимание, что я использую новую тему здесь. Здесь есть несколько опций, кроме NewThread (это будет отдельный поток, не связанный с тем, который обрабатывает пользовательский интерфейс):

NewThread создаст экземпляр планировщика в новом рабочем потоке.

CurrentThread выполнит запланированное действие в потоке пользовательского интерфейса.

Диспетчер позволит вам взаимодействовать с компонентами пользовательского интерфейса, не замораживая основной макет пользовательского интерфейса — это хорошо, если вы хотите передать выходные данные в поток пользовательского интерфейса без использования отдельного вызова invoke.

Немедленно выполнит метод в текущем потоке, но в отличие от CurrentThread, он будет запланирован для немедленного выполнения.

ThreadPool довольно очевиден, и если вы раньше работали с многопоточностью в настольных приложениях, вы знаете, что это в основном очередь рабочего потока. В Windows Phone 7 очередь ограничена 25 зарегистрированными одновременными рабочими потоками — остальные будут в очереди, ожидая вызова, когда место станет доступным.

ПРИМЕЧАНИЕ. Кстати, вам не разрешается вызывать SetMaxThreads, как в настольных приложениях. Это в основном введено в целях безопасности и производительности.

Итак, теперь, когда у меня есть экземпляр планировщика, я могу передать Action и TimeSpan:

scheduler.Schedule(new Action(() =>
{
for (int z = 0; z < 1000; z++)
{
Debug.WriteLine(z);
}
}), TimeSpan.FromSeconds(2));

Очевидно, что я использую очень простое действие, чтобы продемонстрировать возможности механизма планирования — вы можете пропустить гораздо более сложный метод. Переданное Действие будет выполняться каждые 2 секунды.

Но это не единственное, что я могу сделать — я мог бы запланировать действие, которое должно выполняться рекурсивно каждый конкретный интервал времени.

Для этого вам необходимо изменить вызов по расписанию, чтобы он соответствовал этой структуре:

scheduler.Schedule (Action <Action <TimeSpan >> action, TimeSpan dueTime);

So in my case, I simply use this line:

scheduler.Schedule(new Action<Action<TimeSpan>>(GetThis), TimeSpan.FromSeconds(3));

So it will be scheduled to run in three seconds. Now, the action itself:

void GetThis(Action<TimeSpan> action)
{
Debug.WriteLine("Test");
action(TimeSpan.FromSeconds(3));
}

Notice how I am calling the passed action (that originates in the main scheduling call) with a new TimeSpan — when it is hit again, the action will be called once again and it will be your responsibility to break this loop. It is a pretty convenient way to check for data updates, for example.

As you see in my sample, I am using TimeSpan to explicitly pass the time period. However, I should also mention that instead of TimeSpan you can easily use DateTimeOffset.