/// <summary> /// 同时探索多个节点。适用于文章节点。注意,此函数仅在查询方面进行了优化, /// 如果其他线程正在探索此节点,则等待此节点探索完毕。 /// </summary> private async Task FetchPapersAsync(IReadOnlyCollection <KgNode> paperNodes) { Debug.Assert(paperNodes != null); // 注意,有些节点可能仍处于 正在探索 的状态。 // 需要在返回前等待这些正在探索的节点。 var nodesToFetch = paperNodes.Where(n => GetStatus(n.Id).TryMarkAsFetching(NodeStatus.PaperFetching)) .ToArray(); if (nodesToFetch.Length == 0) { goto WAIT_FOR_EXPLORATIONS; } await Task.WhenAll(nodesToFetch.Select(n => n.Id) .Partition(SEB.MaxChainedIdCount) .Select(ids => { // 假定 Partition 返回的是 IList / ICollection var idc = (ICollection <long>)ids; return(SearchClient.EvaluateAsync(SEB.EntityIdIn(idc), SEB.MaxChainedIdCount, ConcurrentPagingMode.Pessimistic, page => { if (page.Entities.Count < idc.Count) { Logger.Magik.Warn(this, "批量查询实体 Id 时,返回结果数量不足。期望:{0},实际:{1}。", idc.Count, page.Entities.Count); } foreach (var entity in page.Entities) { ExplorePaperUnsafe(entity); } return Task.CompletedTask; })); })); foreach (var n in nodesToFetch) { GetStatus(n.Id).MarkAsFetched(NodeStatus.PaperFetching); } WAIT_FOR_EXPLORATIONS: //确保返回前,所有 Fetching 的节点已经由此线程或其他线程处理完毕。 var waitResult = await Task.WhenAll(paperNodes.Select(n => GetStatus(n.Id).UntilFetchedAsync(NodeStatus.PaperFetching))); Debug.Assert(waitResult.All(r => r)); }
/// <summary> /// 同时探索多个节点。适用于文章节点。注意,此函数仅在查询方面进行了优化, /// 如果其他线程正在探索此节点,则等待此节点探索完毕。 /// </summary> private async Task FetchPapersAsync(IReadOnlyCollection <KgNode> paperNodes, string constraint) { Debug.Assert(paperNodes != null); if (constraint == null) { await FetchPapersAsync(paperNodes); return; } // 1. 注意,有些节点可能仍处于 正在探索 的状态。 // 需要在返回前等待这些正在探索的节点。 // 2. 很不幸,一旦标记一个节点开始探索后,没有办法再标记为未探索, // 所以在有约束的情况下,只能在内层循环中对节点分别标记。 var nodesToFetch = paperNodes.Where(n => GetStatus(n.Id).GetFetchingStatus(NodeStatus.PaperFetching) == FetchingStatus.Unfetched) .ToArray(); if (nodesToFetch.Length == 0) { goto WAIT_FOR_EXPLORATIONS; } await Task.WhenAll(nodesToFetch.Select(n => n.Id) .Partition(SEB.MaxChainedIdCount) .Select(ids => { // 假定 Partition 返回的是 IList / ICollection var idc = (ICollection <long>)ids; return(SearchClient.EvaluateAsync(SEB.And(SEB.EntityIdIn(idc), constraint), SEB.MaxChainedIdCount, ConcurrentPagingMode.Optimistic, async page => { foreach (var entity in page.Entities) { await ExplorePaperAsync(entity); } })); })); WAIT_FOR_EXPLORATIONS: ; }