8

使用 Azure 存储客户端库 2.1,我正在对表存储进行异步查询。我创建了这段代码:

public async Task<List<TAzureTableEntity>> GetByPartitionKey(string partitionKey)
{
    var theQuery = _table.CreateQuery<TAzureTableEntity>()
                         .Where(tEnt => tEnt.PartitionKey == partitionKey);
    TableQuerySegment<TAzureTableEntity> querySegment = null;
    var returnList = new List<TAzureTableEntity>();
    while(querySegment == null || querySegment.ContinuationToken != null)
    {
        querySegment = await theQuery.AsTableQuery()
                                     .ExecuteSegmentedAsync(querySegment != null ?
                                         querySegment.ContinuationToken : null);
        returnList.AddRange(querySegment);
    }
    return returnList;
}

让我们假设有大量数据返回,因此会有很多往返表存储。我遇到的问题是我们正在等待一组数据,将其添加到内存列表中,等待更多数据,将其添加到同一个列表中,等待更多数据,将其添加到列表中......等等等等。为什么不将 Task.Factory.StartNew() 包装在常规 TableQuery 周围?像这样:

public async Task<List<TAzureTableEntity>> GetByPartitionKey(string partitionKey)
{
    var returnList = await Task.Factory.StartNew(() =>
                                                 table.CreateQuery<TAzureTableEntity>()
                                                .Where(ent => ent.PartitionKey == partitionKey)
                                                .ToList());
    return returnList;
}

这样做似乎我们并没有来回弹跳 SynchronizationContext 这么多。还是真的很重要?

编辑改写问题

上面提到的两种情况有什么区别?

4

2 回答 2

8

两者之间的区别在于,您的第二个版本将ThreadPool在查询执行的整个过程中阻塞一个线程。这在 GUI 应用程序中可能是可以接受的(您只想在 UI 线程以外的地方执行代码),但它会抵消async服务器应用程序中的任何可伸缩性优势。

此外,如果您不希望您的第一个版本在每次往返时都返回 UI 上下文(这是一个合理的要求),请在使用ConfigureAwait(false)时使用await

querySegment = await theQuery.AsTableQuery()
                             .ExecuteSegmentedAsync(…)
                             .ConfigureAwait(false);

这样,第一次迭代之后的所有迭代都将(很可能)在ThreadPool线程上执行,而不是在 UI 上下文中执行。

顺便说一句,在您的第二个版本中,您实际上根本不需要await,您可以直接返回Task

public Task<List<TAzureTableEntity>> GetByPartitionKey(string partitionKey)
{
    return Task.Run(() => table.CreateQuery<TAzureTableEntity>()
                               .Where(ent => ent.PartitionKey == partitionKey)
                               .ToList());
}
于 2013-10-28T12:28:36.627 回答
3

不确定这是否是您正在寻找的答案,但我仍然想提及它:)。

您可能已经知道,第二种方法(使用Task)在内部处理延续标记,并在获取所有实体后退出该方法,而第一种方法获取一组实体(最多 1000 个),然后给出你的结果集以及一个延续令牌。

如果您有兴趣从表中获取所有实体,则可以使用这两种方法,但是第一种方法可以让您灵活地随时优雅地跳出循环,而第二种方法是您无法获得的。因此,使用第一个功能,您基本上可以引入分页概念。

假设您正在构建一个显示表格数据的 Web 应用程序。进一步让我们假设该表包含大量实体(比如说 100000 个实体)。使用第一种方法,您可以只获取 1000 个实体并将结果返回给用户,如果用户想要,您可以获取下一组 1000 个实体并将它们显示给用户。您可以继续这样做,直到用户想要的时间并且表中有数据。使用第二种方法,用户必须等到从表中获取所有 100000 个实体。

于 2013-10-28T09:30:27.983 回答