Индивидуальное ожидание против задачи. Когда все - PullRequest
0 голосов
/ 28 мая 2018

У меня есть два следующих метода, которые дают одинаковые результаты.

public static async Task<IEnumerable<RiskDetails>> ExecuteSqlStoredProcedureSelect<T>(IEnumerable<AccountInfo> linkedAccounts, string connectionString, string storedProcedure, int connTimeout = 10)
    {

        var responseList = new List<RiskDetails>();

        using (IDbConnection conn = new SqlConnection(connectionString))
        {
            foreach (var account in linkedAccounts)
            {
                var enumResults = await conn.QueryAsync<RiskDetails>(storedProcedure, 
                    new { UserID = account.UserID, CasinoID = account.CasinoID, GamingServerID = account.GamingServerID, AccountNo = account.AccountNumber, Group = account.GroupCode, EmailAddress = account.USEMAIL }, 
                    commandType: CommandType.StoredProcedure);

                if (enumResults != null)
                        foreach (var response in enumResults)
                            responseList.Add(response);
            }
         }

         return responseList;
    }

    public static async Task<IEnumerable<RiskDetails>> ExecuteSqlStoredProcedureSelectParalel<T>(IEnumerable<AccountInfo> linkedAccounts, string connectionString, string storedProcedure, int connTimeout = 10)
    {
        List<Task<IEnumerable<RiskDetails>>> tasks = new List<Task<IEnumerable<RiskDetails>>>();
        var responseList = new List<RiskDetails>();

        using (IDbConnection conn = new SqlConnection(connectionString))
        {
            conn.Open();
            foreach (var account in linkedAccounts)
            {
                var enumResults = conn.QueryAsync<RiskDetails>(storedProcedure,
                        new { UserID = account.UserID, CasinoID = account.CasinoID, GamingServerID = account.GamingServerID, AccountNo = account.AccountNumber, Group = account.GroupCode, EmailAddress = account.USEMAIL },
                        commandType: CommandType.StoredProcedure, commandTimeout: 0);

                //add task
                tasks.Add(enumResults);
            }

            //await and get results
            var results = await Task.WhenAll(tasks);
            foreach (var value in results)
                foreach (var riskDetail in value)
                    responseList.Add(riskDetail);

        }

        return responseList;
    }

Мое понимание того, как ExecuteSqlStoredProcedureSelect выполняется, заключается в следующем:

  • Выполнение запроса для учетной записи № 1
  • Ожидание результата запроса № 1
  • Результат получения запроса № 1
  • Выполнение запроса для учетной записи № 2
  • Ожидание результата запроса № 2
  • и т. Д.

Мое понимание того, как ExecuteSqlStoredProcedureSelectParalel выполняется, заключается в следующем:

  • Добавить все задачи в экземпляр IEnumberable
  • Вызов Task.WhenAll, который начнет выполнять запросы дляУчетная запись # n
  • Запросы выполняются относительно параллельно с SQL-сервером
  • Task.WhenAll возвращает, когда все запросы выполняются

Насколько я понимаю, ExecuteSqlStoredProcedureSelectParalel должно бытьнебольшое улучшение по сравнению с этой функцией по времени, но на данный момент ее нет.

Мое понимание этого неверно?

Ответы [ 2 ]

0 голосов
/ 28 мая 2018

Ваше понимание ExecuteSqlStoredProcedureSelectParalel не совсем правильно.

Вызовите Task.WhenAll, который начнет выполнять запросы для учетной записи # n

Task.WhenAll.ничего не начинать.После возврата метода QueryAsync - задача уже запущена и выполняется или даже завершена.Когда управление достигает Task.WhenAll - все задачи уже запущены.

Запросы выполняются относительно параллельно с сервером SQL

Это сложный вопрос.Чтобы иметь возможность выполнять несколько запросов по одному и тому же соединению sql одновременно - у вас в строке подключения включена опция MultipleActiveResultSets, она не будет работать (выбрасывать исключение) без этого.

Тогда во многих местах, включая документация , вы можете прочитать, что MARS не о параллельном выполнении.Речь идет о чередовании операторов, что означает, что SQL Server может переключаться между различными операторами, выполняющимися по одному и тому же соединению, подобно тому, как ОС может переключаться между потоками (на одном ядре).Цитата из приведенной выше ссылки:

Операции MARS выполняются синхронно на сервере .Допускается чередование операторов операторов SELECT и BULK INSERT.Однако операторы языка манипулирования данными (DML) и языка определения данных (DDL) выполняются атомарно.Любые операторы, пытающиеся выполняться во время выполнения атомарного пакета, блокируются. Параллельное выполнение на сервере не является функцией MARS .

Теперь, , даже если ваши запросы выбора выполняются параллельно на сервере, это не поможетВы много с точки зрения "производительности", если эти запросы выполняются быстро.

Предположим, вы запрашиваете 10 учетных записей, и каждое выполнение запроса занимает 1 мс (вполне нормально, я бы сказал, ожидаемая ситуация).Но каждый запрос возвращает, скажем, 100 строк.Теперь эти 100 строк должны быть доставлены вызывающей стороне по сети.Это самая дорогая часть, время выполнения ничтожно мало по сравнению с этим (в этом конкретном примере).Используете ли вы MARS или нет - у вас есть только одно физическое соединение с сервером SQL.Даже если ваши 10 запросов выполняются параллельно на сервере (что я сомневаюсь из-за вышеизложенного) - их результаты не могут быть доставлены вам параллельно, потому что у вас есть одно физическое соединение.Таким образом, 10 * 100 = 1000 строк в обоих случаях доставляются вам «последовательно».

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

Я также хочу добавить, что количество физических ядер на вашей машине НЕТ ничем не пренебрежимо влиять на производительность вэта ситуация.Асинхронный ввод-вывод не связан с блокировкой потоков, и вы можете читать во многих местах через Интернет.

0 голосов
/ 28 мая 2018

Что ж, вы понимаете, что все правильно, но вам нужно понимать базовые ядра, количество физических ядер, которые есть у вашей машины.

Вы можете создать несколько задач за один раз, но это не означает, что все эти задачи выполняются в parellel, каждая задача представляет поток и запланировано получение на физическом ядре, в свою очередь, одно ядро ​​запускает один поток за раз.

Таким образом, если на вашем компьютере запущено 4 ядра, и вы создали 8 потоков, тогда ваш mahcine будет запускать только 4 потока, а другой поток получит очередь, когда поток запланирован на ядре, если запущенный поток заблокирован или находится в состоянии ожидания или завершен,

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

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

Добавление к вышеупомянутой параллельной библиотеке задачПод капотом используют Threadpool, а потоки в пуле потоков рекомендуется использовать для небольших операций.поскольку длительная операция может потреблять ваш пул потоков, а затем ваша короткая операция должна ждать завершения потока, что также замедляет работу вашего приложения.Поэтому рекомендуется создавать задачу с помощью TaskCreationOptions.LongRunning или использовать async/await, чтобы поток потоков не получал cosume для длительных операций (операция с базой данных, операция чтения / записи файла или внешний вызов web / webservcie для получения данных).


Помимо вышеуказанного в вашем коде,

 var results = await Task.WhenAll(tasks);

это означает ожидание завершения выполнения всех задач, что означает, что если у вас есть 5 задач и 3 из них завершены, но 2из них требуется больше времени для завершения, затем код будет ждать завершения двух длительных задач перед выполнением следующей строки.


Проверьте также: можно ли совместно использовать одно соединение SQL Server для задач, выполняемых параллельно

Соединение SQLServer может совместно использоваться несколькими задачами, выполняющимися параллельноНапример, потоки в программе на C # или запросы на сервере приложений.Но в большинстве сценариев использования вам потребуется синхронизировать доступ к Соединению.Задача должна будет ждать соединения, если другая задача использует его.К тому времени, когда вы создадите механизм общего подключения, который не нарушает и не становится ограничением производительности для ваших параллельных задач, вы, вероятно, уже создали пул соединений.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...