Реализация сценариев NPC без сохранения состояния с помощью сопрограмм/ожидания C# 5

Я пытаюсь реализовать сценарии NPC, используя новую функцию C # await. Это мое доказательство концепции.

В NPC.cs вы можете увидеть этот фрагмент:

public async void Run(INPC npc)
{
    npc.Say("Hello!");
    await npc.WaitForOk();
    npc.Say("This is an example of some weird crap.");
    await npc.WaitForOk();
    npc.Say("Bye.");
    await npc.WaitForOk();
}

В реальном примере сценарий будет реализован на языке сценариев, таком как IronPython. Несмотря на то, что в будущем он может поддерживать ключевые слова async/await, необходимость делать это для каждого вызова очень хлопотна и раздражает.

Я пробовал, чтобы другой метод был асинхронным и выполнял ожидание, а скрипт просто вызывал его, но из-за того, как работает async/await, метод скрипта (Run) просто продолжался бы без паузы/блокировки.

Есть ли способ избежать необходимости делать метод сценария асинхронным и использовать await перед каждым вызовом, сохраняя при этом функциональность, подобную сопрограмме?

Кроме того, если для этого уже доступно лучшее решение, чем использовать async/await, сохраняя при этом эффективность потоков, выделите его.

Спасибо!


person angelsl    schedule 29.12.2011    source источник


Ответы (2)


Это очень интересное использование для async/await.

Я собрал множество связанных с синтаксисом вопросов со всего Интернета в одна из моих записей в блоге. Короче говоря, есть веские причины, по которым необходимы async и await.

Если вы хотите добавить состояние к своим NPC, рассмотрите возможность использования подхода на основе наблюдателя (Rx ). Этот подход способен к сложным взаимодействиям, а также может позволить параллельное выполнение разных NPC.

Вы также можете настроить своих NPC на задачи пула потоков, которые довольно эффективны — когда они блокируются, ЦП становится доступным для других задач.

person Stephen Cleary    schedule 29.12.2011
comment
Вы правы - потоки пула потоков будут заблокированы. Я хотел сказать, что процессор доступен. - person Stephen Cleary; 30.12.2011
comment
Ах.. это имеет больше смысла. Но в реальном приложении могут одновременно происходить сотни или тысячи разговоров NPC; наличие такого количества заблокированных потоков одновременно просто кажется ... «хакерским». Однако я проверю Rx, но подожду других ответов, прежде чем выбрать принятый ответ. Спасибо! - person angelsl; 30.12.2011
comment
@angelsl, это не только хакерство, но и очень плохая идея. Каждый поток потребляет 1 МБ памяти (по умолчанию). Таким образом, наличие тысячи NPC, каждый со своим потоком, означает ненужное потребление 1 ГБ памяти. THreadPool действительно не был предназначен для таких вещей. - person svick; 30.12.2011
comment
@svick: Я тоже так думал. Ах хорошо. - person angelsl; 30.12.2011

Я не понимаю, почему ты так стараешься избегать await. Я думаю, что это хорошая идея, чтобы требовать его для такого типа кода, потому что иначе было бы трудно сказать, что код на самом деле делает. Итак, я считаю, что использование await таким образом, вероятно, является лучшим вариантом.

Кроме того, рекомендуется по возможности избегать методов async void, поскольку вы не можете перехватывать исключения, которые они генерируют.

Я могу себе представить, что есть способы избежать написания async, например:

npc.AddAction(n => n.Say("Hello!"))
   .AddAction(n => n.WaitForOk())
   .AddAction(n => n.Say("This is an example of some weird crap."))
   .AddAction(n => n.WaitForOk());

npc.Run();

Здесь Run() обрабатывает список действий, созданный с помощью AddAction(), и использует await там, где это необходимо.

Но я сомневаюсь, что вы можете сделать это так же просто, как ваш код с awaits. (Кроме того, реализация любого вида потока управления в таком коде сделает его действительно нечитаемым.)

person svick    schedule 30.12.2011
comment
Я стараюсь избегать использования await везде, потому что на реальном игровом сервере сценарии будут реализованы с помощью чего-то вроде IronPython или IronRuby, и он может поддерживать или не поддерживать await/async. Кроме того, методы WaitForblah будут использоваться в контексте, и будет известно, что они «приостановят» выполнение скрипта. Вот почему я стараюсь избегать await везде. - person angelsl; 30.12.2011
comment
Если бы все было так просто, то await не было бы необходимости, не так ли? Что await делает, что полностью переписывает код метода. И вы в основном спрашиваете, как это сделать на языке, который этого не поддерживает. Ну, без использования некоторых плохих хаков, вы не можете. - person svick; 30.12.2011