@Quttar72
Изучаю asp.net core mvc

Объясните почему этот метод работает именно так?

Я сразу извиняюсь за неточную формулировку вопроса, но не смог как-то конкретно его сформулировать. Вопрос по асинхронности. Я сразу привожу код, думаю для программиста средней руки он более чем понятен, но вот я не совсем пойму результат его вывода.

Вот код:
class Program
    {
        static Random random = new Random();

        static void Main(string[] args)
        {
            Console.WriteLine("Сколько запросов отправить?");
            int count = int.Parse(Console.ReadLine());
            Task<string>[] taskMas = new Task<string>[count];


            for (int i = 0; i < count; i++)
            {
                taskMas[i] = Task.Run<string>(() => GetData(i));
            }
            Console.WriteLine("Все задачи отправлены");

            Task.WaitAll(taskMas);
            foreach(Task<string> task in taskMas)
            {
                Console.WriteLine(task.Result);
            }

            Console.WriteLine("Программа выполнилась");
        }
    
        static string GetData(int id)
        {
            int delay = random.Next(0, 1500);
            int myId = id;
            Thread.Sleep(delay);
            return $"Запрос {myId} выполнялся {delay}";
        }
    }


Есть как-бы запросы, которые могут выполняться случайное время. При этом, я не жду пока выполниться каждый запрос, а получаю, как удобно это представлять - "обязательства" по выполнению каждого запроса, т.е. метода, которые выдаются сразу. Потом я жду с помощью метода WaitAll когда все обязательства (Task) будут выполнены и я запрашиваю у каждого из них результат работы.

Вот результат работы программы:
Сколько запросов отправить?
10
Все задачи отправлены
<Вот тут идет пауза пока методы выполняются>
Запрос 10 выполнялся 162
Запрос 10 выполнялся 171
Запрос 10 выполнялся 1267
Запрос 10 выполнялся 307
Запрос 10 выполнялся 184
Запрос 10 выполнялся 722
Запрос 10 выполнялся 762
Запрос 10 выполнялся 101
Запрос 10 выполнялся 1424
Запрос 10 выполнялся 1044
Программа выполнилась


Здесь похоже какая-то банальная вещь, которую я не могу понять, почему id то одинаковые? Я вроде бы уже и сохраняю их в отдельной переменной в методе, но они все равно равняются последнему идентификатору с которым был вызван данный метод. Но смущает, что задержка, то абсолютно честно отработала и у всех запросов она разная. Вот подскажите, что я здесь не верно сделал?
  • Вопрос задан
  • 85 просмотров
Решения вопроса 1
freeExec
@freeExec
Участник OpenStreetMap
Потому что по факту ты не передаёшь значение счётчика как аргумент (метод же тут же не выполняется), ты "захватываешь" переменную, которая там где-то потом сработает. Так вот к моменту когда дело до этого доходит, счётчик уже равен 10, его ты и видишь.

Как вариант ты можешь например добавить промежуточную переменную j, которую ты приравниваешь к i и отдавать её. В общем тебе надо создать отдельную структуру куда ты сложишь параметры для таска и отдашь его уже туда.

П.С.

Если вот глянуть что на самом деле выходит после компиляции
private sealed class <>c__DisplayClass0_0
    {
        public int i;

        internal string <Main>b__0()
        {
            return GetData(i);
        }
    }

    public static void Main()
    {
        int num = 5;
        Task<string>[] array = new Task<string>[num];
        <>c__DisplayClass0_0 <>c__DisplayClass0_ = new <>c__DisplayClass0_0();
        <>c__DisplayClass0_.i = 0;
        while (<>c__DisplayClass0_.i < num)
        {
            array[<>c__DisplayClass0_.i] = Task.Run(new Func<string>(<>c__DisplayClass0_.<Main>b__0));
            <>c__DisplayClass0_.i++;
        }
    }

то видно, что счётчик цикла это поле скрытого класса, у которого всего 1 экземпляр, который создаётся до цикла. И наш делегат для такс читает значение этого поля, когда доходит его очередь выполняться. К тому времени счётчик достигает конца.

Если же мы создаём промежуточную переменную, то в коде уже для каждого такса создаётся свой индивидуальный экземпляр скрытого класса, поэтому и значение будет то, которое было.

int num2 = 0;
        while (num2 < num)
        {
            <>c__DisplayClass0_0 <>c__DisplayClass0_ = new <>c__DisplayClass0_0();
            <>c__DisplayClass0_.j = num2;
            array[num2] = Task.Run(new Func<string>(<>c__DisplayClass0_.<Main>b__0));
            num2++;
        }
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы