Асинхронный метод с параметрами, вызываемыми из нескольких задач, вызывает конфликт - PullRequest
0 голосов
/ 26 апреля 2019

Я новичок в асинхронном программировании и пытаюсь загрузить, скажем, 2 файла в остальные API параллельно.Процесс загрузки для каждого файла состоит из 3-х оставшихся вызовов 1. Начальный этап: создание файла 2. Добавление данных с помощью Patch 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 вызова для контрагентов, но не для ProductCategories, и, следовательно, действие сброса на ProductCategories завершается неудачно, потому что в нем не было добавлений.rst.

По сути, происходит то, что имя файла параметров в моей асинхронной функции перезаписывается в задачах.

Как я могу убедиться, что 2 файла работают с этим набором из 3 вызовов REST впараллельно, без перезаписи переменных в другой задаче

1 Ответ

2 голосов
/ 26 апреля 2019

Использование переменной url не является потокобезопасным. Я вижу, что он определен вне вашего метода, но затем вы используете его и изменяете в методе.

Когда у вас есть два асинхронных прогона, используя и изменяя одну и ту же переменную, вы получаете условия гонки.

...