首页 > 解决方案 > 带有从多个任务调用的参数的异步方法导致冲突

问题描述

我是异步编程的新手,我正在尝试将 2 个文件并行上传到一个 rest api。每个文件的上传过程包括 3 个休息调用 1. 初始放置:创建文件 2. 使用补丁附加数据 3. 保存/刷新文件中的数据以提交。

所有这些都是按顺序进行的,但是当我尝试异步执行时,我会遇到随机失败,因为异步方法的参数会被其他任务覆盖。

我一直在关注这里的指南:https ://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/并尝试遵循类似的策略

        private async Task<string> UploadFiles(string year, string month, string filename, string accesstoken, string path)
        {
            //Initial Put : corresponding to point 1 above in the summary
            var method = new HttpMethod("PUT");
            url = String.Format("https://someurl/part1/{0}/{1}/{2}?resource=file&recursive=True", year, month, localfilename);


            var request = new HttpRequestMessage(method, url)
            {
                Content = null
            };

            request.Headers.Add("Authorization", "Bearer " + accesstoken);


            var initput = await client.SendAsync(request);

            //Append Data :corresponding to point 2 above in the summary
            url = String.Format("https://someurl/part1/{0}/{1}/{2}?action=append&position=0", year, month, localfilename);

            ****Here some code for file details which isn't necessary for this question***

            method = new HttpMethod("PATCH");

            request = new HttpRequestMessage(method, url)
            {
                Content = content
            };



            request.Headers.Add("Authorization", "Bearer " + accesstoken);

            var response = await client.SendAsync(request);

            long? position = request.Content.Headers.ContentLength;

            //Flush Data:corresponding to point 3 above in the summary

            url = String.Format("someurl/part1/{0}/{1}/{2}?action=flush&position={3}", year, month, localfilename, position.ToString());

            request = new HttpRequestMessage(method, url)
            {
                Content = null
            };

            request.Headers.Add("Authorization", "Bearer " + accesstoken);


            response = await client.SendAsync(request);

            return filename;

        }

在调用者中,我使用多个任务调用它,以便它们同时启动然后等待

            Task<string> uploadCounterpartFileTask = UploadFiles(year, month, filename_counterparts, accesstoken, path);
            Task<string> uploadProductCategoryFileTask = UploadFiles(year, month, filename_productcategories, accesstoken, path);

            var allTasks = new List<System.Threading.Tasks.Task> { uploadCounterpartFileTask, uploadProductCategoryFileTask };


            while (allTasks.Any())
            {
                System.Threading.Tasks.Task finished = await System.Threading.Tasks.Task.WhenAny(allTasks);
                if (finished == uploadCounterpartFileTask)
                {
                    allTasks.Remove(uploadCounterpartFileTask);
                    var counterpartFile = await uploadCounterpartFileTask;
                }
                else if (finished == uploadProductCategoryFileTask)
                {
                    allTasks.Remove(uploadProductCategoryFileTask);
                    var productCategoriesFile = await uploadProductCategoryFileTask;
                }
            }

我期望这一点(尽管不是按任何特定顺序,而是在特定文件中排序或在任务中排序)。

所以,如果我检查 Fiddler,我希望:

/part1/2019/02/Counterparts.avro?resource=file&recursive=True
/part1/2019/02/ProductCategories.avro?resource=file&recursive=True
/part1/2019/02/ProductCategories.avro?action=append&position=0
/part1/2019/02/Counterparts.avro?action=append&position=0
/part1/2019/02/ProductCategories.avro?action=flush&position=1664
/part1/2019/02/Counterparts.avro?action=flush&position=30907958

但我得到:

/part1/2019/02/Counterparts.avro?resource=file&recursive=True
/part1/2019/02/ProductCategories.avro?resource=file&recursive=True
/part1/2019/02/Counterparts.avro?action=append&position=0
/part1/2019/02/Counterparts.avro?action=append&position=0
/part1/2019/02/ProductCategories.avro?action=flush&position=1664
/part1/2019/02/Counterparts.avro?action=flush&position=30907958

因此,如果您看到“action=append”,我会收到 2 次针对 Counterparts 的调用,但没有针对 ProductCategories 的调用,因此 ProductCategories 上的刷新操作失败,因为它首先没有发生追加。

基本上发生的事情是我的异步函数中的参数文件名在任务中被覆盖。

如何确保这 2 个文件并行操作这组 3 个 REST 调用,而不覆盖其他任务中的变量

标签: c#asynchronousasync-await

解决方案


您对 url 变量的使用不是线程安全的。我看到它是在您的方法之外定义的,但是您使用它并在整个方法中更改它。

当您有两次异步运行时,使用和更改相同的变量您会得到竞争条件。


推荐阅读