Что мне нужно сделать
Мне нужно запустить разные экземпляры класса в синхронном контексте, используя асинхронный метод.
Структура приложения
В моем приложении console
я объявил List<Bot>
класс:
private List<Bot> _bots = new List<Bot>(new Bot[10]);
класс Bot
содержит некоторые методы, которые получают данные из Интернета, поэтому эти методы нужно ждать. Структура метода выглядит следующим образом:
public class Bot
{
Competition Comp { get; set; }
public async Task StartAsync(int instance)
{
string url = "";
//based on the instance I take the data from different source.
switch(instance)
{
case 0:
url = "www.google.com";
break;
case 1:
url = "www.bing.com";
break;
}
//Comp property contains different groups.
Comp.Groups = await GetCompetitionAsync(Comp, url);
if(Comp.Groups.Count > 0)
{
foreach(var gp in group)
{
//add data inside database.
}
}
}
}
класс Competition
имеет следующий дизайн:
public class Competition
{
public string Name { get; set; }
public List<string> Groups { get; set; }
}
Я запускаю все экземпляры класса Bot
, используя следующий код:
for(int i = 0; i < _bots.Count - 1; i++)
{
_bots[i].StartAsync(i);
}
этот код будет вызывать разные времена StartAsync
класса Bot
, таким образом, я могу управлять каждым экземпляром бота, и в конце концов я могу остановить или запустить конкретный экземпляр в отдельном методе.
Проблема
Метод GetCompetitionAsync
Создание List<string>
:
public async Task<List<string>> GetCompetitionAsync(Competition comp, string url)
{
if(comp == null)
comp = new Competition();
List<string> groups = new List<string();
using (var httpResonse = await httpClient.GetAsync(url))
{
string content = await httpResponse.Content.ReadAsStringAsync();
//fill list groups
}
return groups;
}
По сути, этот метод заполнит List<string>
, доступный в Comp
. Теперь, если я выполняю один экземпляр StartAsync
, все работает хорошо, но когда я запускаю несколько экземпляров (как указано выше), объект Comp
(который содержит Competition
) имеет все свойства NULL
.
Похоже, что когда у меня несколько Task
, выполняющих контекст synchronous
, он не ожидает контекст async
, который в этом случае заполняет List<string>
.
Когда код достигает этой строки: if(Competition.Groups.Count > 0)
, я получаю исключение NULL
, потому что Groups
равно нулю, а другие свойства Comp
равны NULL
.
Как мне справиться с этой ситуацией?
UPDATE
После других попыток я решил создать List<Task>
вместо List<Bot>
:
List<Task> tasks = new List<Task>(new Task[10]);
тогда вместо:
for(int i = 0; i < _bots.Count - 1; i++)
{
_bots[i].StartAsync(i);
}
Я сделал:
for (int i = 0; i < tasks.Count - 1; i++)
{
Console.WriteLine("Starting " + i);
if (tasks[i] == null)
tasks[i] = new Task(async () => await new Bot().StartAsync(i));
видимо все работает хорошо, ошибок нет. Проблема в том, почему? Я думаю, что-то вроде deadlock
, что я даже не могу решить, используя ConfigureAwait(false);
.
Последнее решение также не позволяет мне получить доступ к методу Bot
, потому что теперь это Task
.
ОБНОВЛЕНИЕ 2
Хорошо, возможно, я понял проблему. По сути, await
внутри асинхронного метода StartAsync
пытается вернуться в основной поток, в то время как основной поток занят ожиданием завершения задачи, и это создаст deadlock
.
Вот почему перемещение StartAsync()
внутри List<Task>
работает, потому что теперь вызов async
теперь выполняется в потоке пула потоков, он не пытается вернуться к основному потоку, и все кажется к работам. Но я не могу использовать это решение по причинам, изложенным выше.