C#, Программирование

Асинхронный метод обработки данных

Время прочтения: 3 мин.

Никто не любит ждать! Для суровых аудиторов, тренированных многозадачностью, формализованными процессами, планами и сроками, эта аксиома особенно актуальна.

В сообществе NewTechAudit регулярно предлагаются методы обработки уже полученной информации. Давайте обратим внимание на способы получения массивов данных для обработки.

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

Рассмотрим возможность оптимизации процесса выгрузки данных с использованием асинхронного метода. Данный метод позволяет выполнять одновременно несколько задач без необходимости ожидания результата их выполнения.

Рассмотрим пример. Необходимо получить данные: о событии из базы данных событий EventDescDataBase, о времени выполнения этого события из базы данных EventTimestampDataBase, о инициаторе события из базы данных EventOwnerDataBase при условии, что все три базы находятся на разных серверах. Стандартный подход заключается в том, чтобы последовательно подключиться к каждой базе данных и выгрузить оттуда необходимую информацию (Рис.1):

В этом случае общее время выполнения будет равно суммарному времени выполнения всех операций. Например, запрос к первой базе выполнился за 2 сек., ко второй — за 500 мсек., и к третьей — за 1.2 сек. Общее время выполнения составит 3.7 сек.

При использовании асинхронного метода все три запроса к базам начнут выполняться одновременно (Рис.2):

В этом случае время выполнения запроса будет равно максимальному времени выполнения всех операций. Максимальное время выполнения – 2сек., следовательно, и время выполнения этого процесса будет равно 2сек.

Для реализации этого метода воспользуемся языком с# и стандартной библиотекой System.Threading.Tasks.

Вынесем запрос данных из базы в отдельный метод:

static DataSet GetDataFromDB(string connectionString, string commandText)
        {
            using (var connection = new SqlConnection(connectionString))
            {
                var command = new SqlCommand();
                command.CommandText = commandText;
                var data = new DataSet();
                var adapter = new SqlDataAdapter(command);
                adapter.Fill(data);
                return data;
            }
        }

Для того, чтобы метод запускался асинхронно (все задачи Task, в нем объявленные, запускались параллельно), добавим в объявление метода модификатор async:

static async void GetData() {}

Теперь мы можем вызвать метод Task.Run() для каждой базы, получить для каждого свой набор данных DataSet, и собрать их в один результирующий набор:

DataSet result = new DataSet();

            var task1 = Task.Run(() =>
            {
                var data = GetDataFromDB(EventDescConnectionString, "select Desc from EventDesc where EventID = 123");
                if (data.Tables.Count > 0)
                {
                    foreach (DataTable table in data.Tables)
                    {
                        result.Tables.Add(table);
                    }
                }
            });

            var task2 = Task.Run(() => {
                var data = GetDataFromDB(EventTimeStampConnectionString, "select Timestamp from EventTimestamp where EventID = 123");
                if (data.Tables.Count > 0)
                {
                    foreach (DataTable table in data.Tables)
                    {
                        result.Tables.Add(table);
                    }
                }
            });

            var task3 = Task.Run(() => {
                var data = GetDataFromDB(EventOwnerConnectionString, "select Owner from EventOwner where EventID = 123");
                if (data.Tables.Count > 0)
                {
                    foreach (DataTable table in data.Tables)
                    {
                        result.Tables.Add(table);
                    }
                }
            });

Необходимо дождаться когда все три процесса закончат работу с помощью метода Task.WaitAll(Task[]) и модификатора await:

await Task.WhenAll(new[] { task1, task2, task3 });

В результате использования асинхронного метода мы почти в 2 раза сократили время выполнения операции получения данных и теперь можем приступать к их обработке.

От себя хочу добавить, что этот метод хорош при работе с базами данных, когда критично время отработки скрипта и при запросе большого объема данных.  Применение асинхронного метода всегда сэкономит вам время для решения приоритетных задач.

Пробуйте, тестируйте, делитесь впечатлениями!

Советуем почитать