我们从Dapper请求数千个对象并达到参数限制(2100),因此决定以块的形式加载它们。
我认为这是一个尝试Async Await的好机会 - 这是我第一次参加,所以也许会让学校男生出错!
断点正在被击中,但整个事情都没有回归。这不是一个错误 - 它似乎只是一切都在黑洞!
请帮忙!
这是我原来的方法 - 它现在调用异步方法
public List<MyObject> Get(IEnumerable<int> ids)
{
return this.GetMyObjectsAsync(ids).Result.ToList();
} //Breakpoint on this final bracket never gets hit
我添加了此方法将id拆分为1000块,然后等待任务完成
private async Task<List<MyObject>> GetMyObjectsAsync(IEnumerable<int> ids)
{
var subSets = this.Partition(ids, 1000);
var tasks = subSets.Select(set => GetMyObjectsTask(set.ToArray()));
//breakpoint on the line below gets hit ...
var multiLists = await Task.WhenAll(tasks);
//breakpoint on line below never gets hit ...
var list = new List<MyObject>();
foreach (var myobj in multiLists)
{
list.AddRange(myobj);
}
return list;
}
以下是任务......
private async Task<IEnumerable<MyObject>> GetMyObjectsTask(params int[] ids)
{
using (var db = new SqlConnection(this.connectionString))
{
//breakpoint on the line below gets hit
await db.OpenAsync();
return await db.QueryAsync<MyObject>(@"SELECT Something FROM Somewhere WHERE ID IN @Ids",
new { ids});
}
}
以下方法只是以块的形式拆分ID列表 - 这似乎工作得很好......
private IEnumerable<IEnumerable<T>> Partition<T>(IEnumerable<T> source, int size)
{
var partition = new List<T>(size);
var counter = 0;
using (var enumerator = source.GetEnumerator())
{
while (enumerator.MoveNext())
{
partition.Add(enumerator.Current);
counter++;
if (counter % size == 0)
{
yield return partition.ToList();
partition.Clear();
counter = 0;
}
}
if (counter != 0)
yield return partition;
}
}
答案 0 :(得分:12)
您正在使用async/await
与Task<T>.Result
结合使用的方式创建死锁情况。
违规行是:
return this.GetMyObjectsAsync(ids).Result.ToList();
GetMyObjectsAsync(ids).Result
会阻止当前的同步上下文。但GetMyObjectsAsync
使用await
来调度当前同步上下文的连续点。我确信你可以看到这种方法的问题:永远不能执行延续,因为当前的同步上下文被Task.Result
阻止。
在某些情况下可以使用的一个解决方案是使用ConfigureAwait(false)
,这意味着可以在任何同步上下文中运行延续。
但总的来说,我认为最好避免使用Task.Result
async/await
。
请注意,实际发生此死锁情况取决于调用异步方法时使用的同步上下文。对于ASP.net,Windows Forms和WPF,这将导致死锁,但据我所知,它不适用于控制台应用程序。 (感谢Marc Gravell的评论)
微软有一篇关于Best Practices in Asynchronous Programming的好文章。 (感谢ken2k)
答案 1 :(得分:0)
我认为参数区分大小写应该是:
return await db.QueryAsync<MyObject>(@"SELECT Something FROM Somewhere WHERE ID IN @ids",
new { ids});
而不是查询中的“@Ids”:
return await db.QueryAsync<MyObject>(@"SELECT Something FROM Somewhere WHERE ID IN **@Ids**",
new { ids});
不确定,但只是尝试。