【发布时间】:2018-11-06 17:56:42
【问题描述】:
我需要做什么
我需要使用异步方法在同步上下文中启动一个类的不同实例。
应用结构
在我的console 应用程序中,我声明了一个List<Bot> 类:
private List<Bot> _bots = new List<Bot>(new Bot[10]);
Bot 类包含一些从 Internet 获取数据的方法,因此需要等待这些方法。方法结构如下:
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);
}
这段代码将调用Bot类的不同时间StartAsync,这样,我可以管理机器人的每个实例,并且我最终可以在单独的方法中停止或启动特定实例。
问题
方法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;
}
基本上,此方法将填充Comp 中可用的List<string>。现在,如果我执行StartAsync 的单个实例一切正常,但是当我运行多个实例(如上)时,Comp 对象(包含Competition)具有NULL 的所有属性。
似乎当我有多个Task 运行synchronous 上下文时,不会等待async 上下文,在这种情况下填充List<string>。
当代码到达这一行时:if(Competition.Groups.Count > 0) 我得到一个NULL 异常,因为Groups 为空,而其他Comp 属性为NULL。
我该如何处理这种情况?
更新
经过其他尝试,我想创建一个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
好吧,也许我遇到了问题。本质上,异步方法StartAsync中的await试图在主线程上恢复,同时主线程正忙于等待任务完成,这将创建一个deadlock。
这就是为什么将StartAsync() 移动到List<Task> 中起作用的原因,因为现在async 调用现在在线程池线程上运行,它不会尝试返回到主线程,一切看起来工作。但由于上述原因,我无法使用此解决方案。
【问题讨论】:
-
向 Competition 类添加一个不接受参数的构造函数,并在该构造函数中将您的
List<string>属性初始化为new List<string>,这应该可以消除 null 异常。 -
@ryanwilson 是的,但这并不能解决问题。
-
@RyanWilson 这无论如何都无法解决问题,如果我启动一个实例,
Groups会正确填充,如果我有多个实例正在运行,我会收到错误,所以你的解决方案是一个修复,但没有真正的修复 -
我写一个好问题的所有努力都白费了,我真的不知道为什么我得到了反对票,至少解释一下
-
我认为您的示例中有错误。
async Task GetCompetitionAsync不会编译。它不应该返回Task<List<string>>吗?
标签: c# multithreading console task