在递归函数中使用Async / Await时控制任务总数

我写了这段代码。 它通过进行REST调用以递归方式在Web系统中创建文件夹。 所以基本上,它为根节点创建一个文件夹,然后并行地并递归地调用自己获取所有子节点。 (为每个孩子)

代码唯一的问题是,如果一个节点也有子节点,或者如果层次结构太深,那么我开始得到“TaskCancellation”错误。

我已经尝试将超时增加到10分钟..但这并没有解决问题。

所以我的问题是如何开始说出50个任务,然后等待某些东西被释放,只有当有50个开放槽时才能继续。

目前我认为我的代码正在创建任务,没有任何限制,因为流经层次结构。

public async Task CreateSPFolder(Node node, HttpClient client, string docLib, string currentPath = null) { string nodeName = Uri.EscapeDataString(nodeName); var request = new { __metadata = new { type = "SP.Folder" }, ServerRelativeUrl = nodeName }; string jsonRequest = JsonConvert.SerializeObject(request); StringContent strContent = new StringContent(jsonRequest); strContent.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json;odata=verbose"); HttpResponseMessage resp = await client.PostAsync(cmd, strContent); if (resp.IsSuccessStatusCode) { currentPath = (currentPath == null) ? nodeName : currentPath + "/" + nodeName; } else { string content = await resp.Content.ReadAsStringAsync(); Console.WriteLine(content); throw new Exception("Failed to create folder " + content); } } List taskList = new List(); node.Children.ToList().ForEach(c => taskList.Add(CreateSPFolder(c, client, docLib, currentPath))); Task.WaitAll(taskList.ToArray()); } 

您可以使用SemaphoreSlim来控制并发任务的数量。 您将信号量初始化为您想要的最大任务数,然后每次执行任务时获取信号量,然后在完成任务后释放它。

这是一个稍微简化的代码版本,它使用随机数永久运行,同时最多执行2个任务。

 class Program { private static SemaphoreSlim semaphore = new SemaphoreSlim(2, 2); public static async Task CreateSPFolder(int folder) { try { await semaphore.WaitAsync(); Console.WriteLine("Executing " + folder); Console.WriteLine("WaitAsync - CurrentCount " + semaphore.CurrentCount); await Task.Delay(2000); } finally { Console.WriteLine("Finished Executing " + folder); semaphore.Release(); Console.WriteLine("Release - CurrentCount " + semaphore.CurrentCount); } var rand = new Random(); var next = rand.Next(10); var children = Enumerable.Range(1, next).ToList(); Task.WaitAll(children.Select(CreateSPFolder).ToArray()); } static void Main(string[] args) { CreateSPFolder(1).Wait(); Console.ReadKey(); } } 

首先,我认为你的问题不是任务量,而是在Task.WaitAll(taskList.ToArray());等待的被阻塞线程数量Task.WaitAll(taskList.ToArray()); 。 在这种情况下最好等待异步(即await Task.WhenAll(taskList);

其次,您可以将TPL DataflowActionBlockMaxDegreeOfParallelism设置为50,并为每个要创建的文件夹发布。 这样你就可以执行一个平坦的工作队列,当它被清空时,你就完成了。

伪代码:

 var block = new ActionBlock( async folderInfo => { await CreateFolderAsync(folderInfo); foreach (var subFolder in GetSubFolders(folderInfo)) { block.Post(subFolder); } }, new DataFlowExecutionOptions {MaxDegreeOfParallelism = 5}); block.Post(rootFolderInfo);