Я попал в ситуацию, которую в "нормальной" среде c #, вероятно, было бы относительно легко решить, но она немного сложнее с предостережениями Unity.
Высокий уровень - это IУ меня есть набор задач, которые нужно выполнить, некоторые из которых могут быть параллельными, а некоторые асинхронными, другие нет.Для тех, кто может быть запущен параллельно, когда каждая задача завершается, мне нужно запустить событие (в главном потоке), чтобы сообщить всем заинтересованным сторонам, что задача была выполнена.
Чтобы углубиться в детали,это для игры в Unity, однако этот конкретный код требует, чтобы он не касался Unity API, как это будет в отдельном проекте с чистым c #.
Концептуально в моей игре яесть "День Перехода".Каждую ночь, когда игрок ложится спать (или теряет сознание от истощения / поражается врагами), игра выходит на экран загрузки, чтобы обрабатывать более длительные операции, которые я не хотел бы выполнять в середине игрового процесса.Такие вещи, как запуск симуляции для обновления ежедневной экономики или файловый ввод-вывод для загрузки вещей, которые будут использоваться в течение следующего дня.
Эти дубли могут быть, а могут и не быть потокобезопасными, и на самом деле могут быть или не быть истинными асинхронными вызовами (например, загрузка файлов будет).
Моя цель - сделать этоспособ, который блокирует основной поток как можно меньше (может произойти некоторое блокирование, это неизбежно, поскольку некоторые задачи могут касаться API Unity, который требуется для выполнения в основном потоке. Это не потокобезопасно).
Поскольку некоторые вещи будут асинхронными, я воспринимаю все как ожидаемое.
В моем классе DayTransitioner есть список объектов IDayTransitionAction:
public interface IDayTransitionAction
{
bool IsRepeating { get; }
bool IsThreadSafe { get; }
Task ProcessTransitionAsync();
}
В действительности существуют два разных процесса, которые должны произойтиВот.
Во-первых, код, который является потокобезопасным (асинхронным или нет), должен иметь возможность запускаться таким образом, чтобы они могли фактически работать параллельно, а по завершении события событие должно запускаться наосновной поток.
protected async Task ProcessThreadSafeTasks()
{
//1st kick off in parallel, probably using one of the Task
//methods such as WhenAll or WhenAny or some other voodoo...
//Not really sure what API calls or structure to use to do it...
//As each comes back an event needs to be fired.
//The event needs to occur on the main thread.
//this.OnIndividualItemProcessed();
}
Вторым является то, что вещи, которые не являются потокобезопасными, нужно запускать и ожидать (я понимаю, что синхронный процесс в этой группе заблокирует основной поток, но когда одиндействительно асинхронно, это не должно).
Второй случай на самом деле легче всего разрешить, потому что Unity предоставляет контекст синхронизации, который вызывает выполнение в главном потоке.Так что последний, я думаю, может просто ждать в цикле.
protected async Task ProcessNonThreadSafeTasks()
{
foreach (var actionItem in this.nonThreadSafeActions)
{
await actionItem.ProcessTransitionAsync();
//Raise the OnIndividualItemProcessed event so eventually
//a loading bar can be filled in as tasks complete, though
//this class doesn't know or care about the loading bar
//specifically. Just that interested parties want to know
//when an individual task is completed
this.OnIndividualItemProcessed();
}
}
Это тот неприятный первый случай, который вызывает у меня головную боль, так как я не совсем уверен, как запустить их таким образом, который позволяетони должны выполняться параллельно, а также отправлять событие по мере завершения каждого отдельного события (особенно учитывая контекст синхронизации Unity ... Я предполагаю, что мне нужно как-то временно отменить это).Как мне выполнить это?
Заранее спасибо!
Кроме того, для ссылки на метод, который вызовет оба из вышеперечисленных ...
public async Task ProcessTransitionActions()
{
//Used primarily to lock down the lists while processing is ongoing.
//The Add method will dump anything that's attempting to be stored
//to another list to be sorted and added to the main lists after processing
//is finished.
IsProcessing = true;
await this.ProcessThreadSafeTasks();
await this.ProcessNonThreadSafeTasks();
//Purge any actions that aren't repeating.
this.CleanNonRepeatingActions();
//Add any actions that were stored while processing to the main lists.
this.SortStoredActions();
IsProcessing = false;
//Send out an event to allow interested parties to know processing is
//finished.
this.OnProcessingCompleted();
}
Редактировать 1 *
Просто для пояснения, «потокобезопасные» операции предназначены для запуска (или, по крайней мере, для выполнения) в других потоках (true-параллельный) в противоположностьпросто одновременно в основной теме.Когда они завершены, и мне нужно отстрелить событие, оно должно быть выделено в основном потоке.