Как правильно реализовать интерфейс, который был разработан для асинхронного использования? - PullRequest
0 голосов
/ 29 октября 2018

Начиная со следующего (упрощенного) интерфейса, который имеет в виду асинхронность / ожидание, я хочу реализовать его с помощью базы данных LiteDB.

public interface IDataService
{
    Task<User> GetUserAsync(int key);
}

К сожалению, LiteDB в настоящее время не поддерживает асинхронные методы. Все методы являются синхронными.

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

public Task<User> GetUserAsync(int key)
{
    var task = Task.Run(() =>
    {
        return _users
            .Find(x => x.Key == key)
            .SingleOrDefault();
    });
    return task;
}

Несмотря на то, что я прочитал несколько статей в блоге, а также вопросы SO, мне все еще неясно, как сделать правильно написанный ожидаемый метод для кода на основе синхронного ввода-вывода.

Так, как я могу написать правильно ожидаемый метод?

Примечание: я не могу изменить интерфейс. Это дано.

Ответы [ 2 ]

0 голосов
/ 29 октября 2018

Вы не должны использовать Task.Run. Вопрос не в том, правильно ли это делать с моральной точки зрения. Как вы заметили, морально неправильно назначать рабочие потоки задачам, которые не связаны с процессором, но здесь это не проблема. Проблема здесь заключается в том, что LiteDB является однопоточным и не создан для того, чтобы быть устойчивым перед лицом попыток перенести в него вызовы на рабочие потоки.

ОБНОВЛЕНИЕ: Очевидно, LiteDB является поточно-ориентированной версией в версии 4, по словам комментатора, который, вероятно, будет более информирован, чем я, по этому вопросу. Итак, обратитесь к документации LiteDB, чтобы узнать, что это за добрый потокобезопасный. Не все потоковобезопасные объекты могут быть использованы без ограничений для любого потока.

Ваш выбор:

  • Отказаться от использования этого интерфейса.
  • Как подсказывает другой ответ, ложь. Скажите, что ваша реализация асинхронна, когда она на самом деле синхронна. Это может привести к зависанию вашего пользовательского интерфейса, но эй, вы собирались повесить свой пользовательский интерфейс в любом случае, когда выполняли синхронный длительный вызов. Явный вызов FromResult является правильным способом сделать это.
  • Получите лучшую базу данных, которая поддерживает асинхронность. Или подождите, пока LiteDB станет лучшей базой данных, поддерживающей асинхронность.
  • Перемещение всех вызовов на LiteDB, включая создание и уничтожение каждого объекта, связанного с этой базой данных, в выделенный поток. (Возможно, в процессе, но эй, если хотите, вы можете поместить его в свой собственный процесс.) Реализовать свой собственный асинхронный интерфейс для LiteDB, который соответствующим образом выполняет маршализацию вызовов в и из этого выделенного потока. Да, вы записываете весь поток, который проводит большую часть времени в спящем режиме, но это цена, которую вы платите за использование синхронной базы данных.

Решение, которое отвечает всем вашим заявленным ограничениям, является последним. Это также тот, который больше всего работает для вас. Как любят говорить плотники: то, что вы экономите на дешевых материалах, вы тратите на рабочую силу. Попытка дооснастить правильный асинхронный интерфейс на не асинхронную, но связанную с вводом / выводом однопоточную библиотеку - сложная проблема. Удачи!

0 голосов
/ 29 октября 2018

Одним из вариантов является использование Task.FromResult и синхронная реализация интерфейса.

return Task.FromResult(_users.Find(x => x.Key == key).SingleOrDefault());

Окупаемость async/await - это использование портов завершения ввода / вывода, которые приостанавливают поток и позволяют ему обслуживать другие входящие запросы до завершения ожидаемой операции ввода / вывода. Видя, как ваша реализация не может использовать эту упаковку, ваша работа в Task.Run или подобном не имеет никакой выгоды.

...