Tasks
От: swimmers  
Дата: 16.04.20 08:45
Оценка:
Коллеги, подскажите, насколько корректно пытаться писать такой код?
Или это в принципе не правильно?

            var tasks = Task[]
            {
                ctx.GetTable<Table1>().ToArrayAsync(),
                ctx.GetTable<Table2>().ToArrayAsync(),
        // Здесь еще подобные строки
            };

            tasks.WaitAll(); // Валится с ошибкой InvalidOperationException: Выполнение команды не может быть продолжено до завершения отложенной асинхронной операции.
Re: Tasks
От: Qulac Россия  
Дата: 16.04.20 08:54
Оценка:
Здравствуйте, swimmers, Вы писали:

S>Коллеги, подскажите, насколько корректно пытаться писать такой код?

S>Или это в принципе не правильно?

S>
S>            var tasks = Task[]
S>            {
S>                ctx.GetTable<Table1>().ToArrayAsync(),
S>                ctx.GetTable<Table2>().ToArrayAsync(),
S>        // Здесь еще подобные строки
S>            };

S>            tasks.WaitAll(); // Валится с ошибкой InvalidOperationException: Выполнение команды не может быть продолжено до завершения отложенной асинхронной операции.

S>


Не особо вникал в асинхронность, но может так надо: Task.WaitAll(tasks) ?
Программа – это мысли спрессованные в код
Re[2]: Tasks
От: swimmers  
Дата: 16.04.20 09:05
Оценка:
Здравствуйте, Qulac, Вы писали:

Q>Не особо вникал в асинхронность, но может так надо: Task.WaitAll(tasks) ?


Это вид сбоку

public static void WaitAll([NotNull] this IEnumerable<Task> tasks) => Task.WaitAll(tasks.ToArray());
Re: Tasks
От: Слава  
Дата: 16.04.20 09:34
Оценка:
Здравствуйте, swimmers, Вы писали:

S>Коллеги, подскажите, насколько корректно пытаться писать такой код?

Чую какую-то фигню с инициализатором. Попробуйте подождать через await Task.WhenAll(task1) где task1 является единственной задачей из ваших. Может сами по себе задачи кривые.
Re: Tasks
От: romangr Россия  
Дата: 16.04.20 10:14
Оценка:
Здравствуйте, swimmers, Вы писали:

S>Коллеги, подскажите, насколько корректно пытаться писать такой код?


Редко какой провайдер позволяет в рамках одного соединения исполнять одновременно несколько команд.
Для MS SQL Server это в принципе возможно, в строке подключения нужно добавить MultipleActiveResultSets=True.
Для остальных = хз, но если очень нужно — делайте 2 подключения или сколько нужно, т.е.
var tasks = Task[]
{
        ctx1.GetTable<Table1>().ToArrayAsync(),
        ctx2.GetTable<Table2>().ToArrayAsync(),
};

tasks.WaitAll();
... << RSDN@Home 1.3.110 alpha 5 rev. 62>>
Re: Tasks
От: Ночной Смотрящий Россия  
Дата: 16.04.20 15:22
Оценка:
Здравствуйте, swimmers, Вы писали:

S>Коллеги, подскажите, насколько корректно пытаться писать такой код?

S>Или это в принципе не правильно?

Не особо правильно. По умолчанию оно будет периодически икдаться исключениями, потому что параллельная работа на одном коннекшене запрещена. Для сиквела можно включить MARS и оно даже заработает, но толку от этого никакого, одни глюки, потому что на сервере запросы все равно сериализуются в последовательную очередь.
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
Re: Tasks
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 16.04.20 15:51
Оценка:
Здравствуйте, swimmers, Вы писали:

S>Коллеги, подскажите, насколько корректно пытаться писать такой код?

S>Или это в принципе не правильно?

S>
S>            var tasks = Task[]
S>            {
S>                ctx.GetTable<Table1>().ToArrayAsync(),
S>                ctx.GetTable<Table2>().ToArrayAsync(),
S>        // Здесь еще подобные строки
S>            };

S>            tasks.WaitAll(); // Валится с ошибкой InvalidOperationException: Выполнение команды не может быть продолжено до завершения отложенной асинхронной операции.

S>


Лучше WhenAll
https://docs.microsoft.com/ru-ru/dotnet/api/system.threading.tasks.task.whenall?view=netframework-4.8

Если какая-либо из переданных задач завершается в состоянии сбоя, возвращаемая задача также завершается в Faulted состоянии, где его исключения будут содержать статистическую обработку набора неупакованных исключений из каждой из предоставляемых задач.
Если ни одна из поставленных задач не завершилась сбоем, но по крайней мере один из них был отменен, то возвращаемая задача завершится в Canceled состоянии.
Если ни одна из задач не была прервана и ни одна из задач не была отменена, то результирующая задача завершится в RanToCompletion состоянии.
Если переданный массив или перечисление не содержит задач, то возвращаемая задача сразу же переходит в состояние RanToCompletion до того, как оно будет возвращено вызывающему объекту.


Там же пример обхода результатов

 if (continuation.Status == TaskStatus.RanToCompletion) {
         long grandTotal = 0;
         foreach (var result in continuation.Result) {
            grandTotal += result;
            Console.WriteLine("Mean: {0:N2}, n = 1,000", result/1000.0);
         }
   
         Console.WriteLine("\nMean of Means: {0:N2}, n = 10,000",
                           grandTotal/10000);
      }
      // Display information on faulted tasks.
      else {
         foreach (var t in tasks) {
            Console.WriteLine("Task {0}: {1}", t.Id, t.Status);
         }
      }



Вот здесь много интересных паттернов
https://docs.microsoft.com/ru-ru/dotnet/standard/asynchronous-programming-patterns/consuming-the-task-based-asynchronous-pattern
и солнце б утром не вставало, когда бы не было меня
Отредактировано 16.04.2020 16:18 Serginio1 . Предыдущая версия . Еще …
Отредактировано 16.04.2020 15:55 Serginio1 . Предыдущая версия .
Re[2]: Tasks
От: swimmers  
Дата: 16.04.20 18:24
Оценка:
Здравствуйте, Слава, Вы писали:

С>Чую какую-то фигню с инициализатором. Попробуйте подождать через await Task.WhenAll(task1) где task1 является единственной задачей из ваших. Может сами по себе задачи кривые.


Одна задача в массиве — работает.
Две и больше — нет.
Re[2]: Tasks
От: swimmers  
Дата: 16.04.20 18:25
Оценка:
Здравствуйте, romangr, Вы писали:

R>Редко какой провайдер позволяет в рамках одного соединения исполнять одновременно несколько команд.

R>Для MS SQL Server это в принципе возможно, в строке подключения нужно добавить MultipleActiveResultSets=True.
R>Для остальных = хз, но если очень нужно — делайте 2 подключения или сколько нужно, т.е.

MARS у меня всегда включен.
Re[2]: Tasks
От: swimmers  
Дата: 16.04.20 18:33
Оценка:
Здравствуйте, Serginio1, Вы писали:


S>Лучше WhenAll

S>https://docs.microsoft.com/ru-ru/dotnet/api/system.threading.tasks.task.whenall?view=netframework-4.8

Тоже падает с
"Выполнение команды не может быть продолжено до завершения отложенной асинхронной операции."
Re[2]: Tasks
От: swimmers  
Дата: 16.04.20 18:35
Оценка:
Здравствуйте, Ночной Смотрящий, Вы писали:

НС>Здравствуйте, swimmers, Вы писали:


НС>Не особо правильно. По умолчанию оно будет периодически икдаться исключениями, потому что параллельная работа на одном коннекшене запрещена. Для сиквела можно включить MARS и оно даже заработает, но толку от этого никакого, одни глюки, потому что на сервере запросы все равно сериализуются в последовательную очередь.


MARS всегда включаю.
Но увы, не работает.
Re[3]: Tasks
От: romangr Россия  
Дата: 17.04.20 06:03
Оценка: 75 (2)
Здравствуйте, swimmers, Вы писали:

S>Здравствуйте, romangr, Вы писали:


R>>Редко какой провайдер позволяет в рамках одного соединения исполнять одновременно несколько команд.

R>>Для MS SQL Server это в принципе возможно, в строке подключения нужно добавить MultipleActiveResultSets=True.
R>>Для остальных = хз, но если очень нужно — делайте 2 подключения или сколько нужно, т.е.

S>MARS у меня всегда включен.


Исключение InvalidOperationException с текстом 'The command execution cannot proceed due to a pending asynchronous operation already in progress.'
кидает SqlCommand, если пытаться выполнить операцию не дожидаясь окончания предыдущей асинхронной операции у того же экземпляра SqlCommand.
Немного покопав исходники linq2db видно, что при выполнении запросов DataConnection не создает на каждый запрос новую DbCommand, а переиспользует уже имеющуюся,
(смотреть реализацию свойства Command и метод InitCommand) что и приводит к такому поведению. Если использовать DataContext, то логика остается той же самой,
т.к. внутри DataContext используется кеширование DataConnection (см. метод DataContext.GetDataConnection) Т.е. работать будет либо такой код
var res1 = await ctx.GetTable<Table1>().ToArrayAsync();
var res2 = await ctx.GetTable<Table2>().ToArrayAsync();

либо такой
var ctx1 = new MyContext();
var ctx2 = new MyContext();
var tasks = Task[]
{
    ctx1.GetTable<Table1>().ToArrayAsync(),
    ctx2.GetTable<Table2>().ToArrayAsync(),
};
await Task.WhenAll(tasks);
... << RSDN@Home 1.3.110 alpha 5 rev. 62>>
Re[4]: Tasks
От: swimmers  
Дата: 17.04.20 07:33
Оценка:
Здравствуйте, romangr, Вы писали:

R>Немного покопав исходники linq2db видно, что при выполнении запросов DataConnection не создает на каждый запрос новую DbCommand, а переиспользует уже имеющуюся,

R>(смотреть реализацию свойства Command и метод InitCommand) что и приводит к такому поведению. Если использовать DataContext, то логика остается той же самой,

Предложенные варианты конечно рабочие, но по факту или бессмысленные, или (кмк) вредные (вариант с новым подключением на каждый запрос).

Хотелось бы услышать комментарии от авторов библиотеки.
Re[3]: Tasks
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 17.04.20 10:00
Оценка:
Здравствуйте, swimmers, Вы писали:

S>Здравствуйте, Serginio1, Вы писали:



S>>Лучше WhenAll

S>>https://docs.microsoft.com/ru-ru/dotnet/api/system.threading.tasks.task.whenall?view=netframework-4.8

S>Тоже падает с

S>"Выполнение команды не может быть продолжено до завершения отложенной асинхронной операции."

Ну это понятно, просто можно повторно запустить задачу которая вывалилась по ошибке. Просто WhenAll
лучше использовать чем WaitAll
и солнце б утром не вставало, когда бы не было меня
Re: Tasks
От: Danchik Украина  
Дата: 19.04.20 20:24
Оценка:
Здравствуйте, swimmers, Вы писали:

S>Коллеги, подскажите, насколько корректно пытаться писать такой код?

S>Или это в принципе не правильно?

S>
S>            var tasks = Task[]
S>            {
S>                ctx.GetTable<Table1>().ToArrayAsync(),
S>                ctx.GetTable<Table2>().ToArrayAsync(),
S>        // Здесь еще подобные строки
S>            };

S>            tasks.WaitAll(); // Валится с ошибкой InvalidOperationException: Выполнение команды не может быть продолжено до завершения отложенной асинхронной операции.

S>

Нельзя такое делать, это сродни запуску одного запроса в пескольких потоках.
DataConnection НЕ потокобезопасен.

Создайте два DataConnection и запустите эти запросы. Тогда будет ок.
Re[2]: Tasks
От: swimmers  
Дата: 20.04.20 06:57
Оценка: -1
Здравствуйте, Danchik, Вы писали:

D>Нельзя такое делать, это сродни запуску одного запроса в пескольких потоках.

D>DataConnection НЕ потокобезопасен.

D>Создайте два DataConnection и запустите эти запросы. Тогда будет ок.


Но ведь я явно не использую потоки.
Я пытаюсь использовать таски.

В библиотеке сделано множество ХххAsync расширений. Но по факту получается, что пользоваться ими в полной мере нельзя. Печально немного.
Re[3]: Tasks
От: Danchik Украина  
Дата: 20.04.20 09:43
Оценка:
Здравствуйте, swimmers, Вы писали:

S>Здравствуйте, Danchik, Вы писали:


D>>Нельзя такое делать, это сродни запуску одного запроса в пескольких потоках.

D>>DataConnection НЕ потокобезопасен.

D>>Создайте два DataConnection и запустите эти запросы. Тогда будет ок.


S>Но ведь я явно не использую потоки.

S>Я пытаюсь использовать таски.

А они типа не используют потоки. Даже одна таска запускается в разных потоках во время своей жизни.
Тут у вас получается что в Task Secheduler добавилось два таска. Один пошел к базе, задержался на IO операции, второй начал работу, тоже полез к базе, нарвался исключение DataProvider.

S>В библиотеке сделано множество ХххAsync расширений. Но по факту получается, что пользоваться ими в полной мере нельзя. Печально немного.


Хотите два паралельно работающих таска — создавайте два конекшина. Их создание дешевая операция.
Мы конечно можем поддержать здесь MARS, но это не лучшее решение, количество таких конекшинов ограничено да и не все провайдеры поддерживают.
Потом захочется два апдейта запустить паралельно — блокировать второй пока первый не прошел? Тогда зачем весь этот цирк c WhenAll?

С нашей стороны было бы неплохо выбросить правильное исключение если это возможно.
Re[4]: Tasks
От: swimmers  
Дата: 20.04.20 10:29
Оценка:
Здравствуйте, Danchik, Вы писали:

D>А они типа не используют потоки. Даже одна таска запускается в разных потоках во время своей жизни.

Разве выделенное всегда верно?
D>Тут у вас получается что в Task Secheduler добавилось два таска. Один пошел к базе, задержался на IO операции, второй начал работу, тоже полез к базе, нарвался исключение DataProvider.

S>>В библиотеке сделано множество ХххAsync расширений. Но по факту получается, что пользоваться ими в полной мере нельзя. Печально немного.


D>Хотите два паралельно работающих таска — создавайте два конекшина. Их создание дешевая операция.

D>Мы конечно можем поддержать здесь MARS, но это не лучшее решение, количество таких конекшинов ограничено да и не все провайдеры поддерживают.

D>С нашей стороны было бы неплохо выбросить правильное исключение если это возможно.


Вот коллега romangr чуть выше покопался в исходниках и пишет:

Немного покопав исходники linq2db видно, что при выполнении запросов DataConnection не создает на каждый запрос новую DbCommand, а переиспользует уже имеющуюся,
(смотреть реализацию свойства Command и метод InitCommand) что и приводит к такому поведению.

Он не прав?
Если создавать по новому DbCommand на запрос — будет работать?

D>Потом захочется два апдейта запустить паралельно — блокировать второй пока первый не прошел? Тогда зачем весь этот цирк c WhenAll?

Блокировки оставить на откуп СУБД и пользователю библиотеки. Как напишет, так СУБД пусть и исполняет.
Re[5]: Tasks
От: takTak  
Дата: 20.04.20 10:36
Оценка:
S>Вот коллега romangr чуть выше покопался в исходниках и пишет:
S>

S>Немного покопав исходники linq2db видно, что при выполнении запросов DataConnection не создает на каждый запрос новую DbCommand, а переиспользует уже имеющуюся,
S>(смотреть реализацию свойства Command и метод InitCommand) что и приводит к такому поведению.

S>Он не прав?
S>Если создавать по новому DbCommand на запрос — будет работать?

D>>Потом захочется два апдейта запустить паралельно — блокировать второй пока первый не прошел? Тогда зачем весь этот цирк c WhenAll?

S>Блокировки оставить на откуп СУБД и пользователю библиотеки. Как напишет, так СУБД пусть и исполняет.

S> ctx.GetTable<Table1>().ToArrayAsync(),


ctx — это ling2db или это Entity Framework ?

для EF рекомендуют делать так :
https://stackoverflow.com/questions/41749896/ef-6-how-to-correctly-perform-parallel-queries
Re[5]: Tasks
От: Sinclair Россия https://github.com/evilguest/
Дата: 20.04.20 16:06
Оценка:
Здравствуйте, swimmers, Вы писали:
S>Хотелось бы услышать комментарии от авторов библиотеки.
А чего именно вы хотите добиться при помощи параллельной загрузки обоих запросов?
На первый взгляд, как раз два await один за другим — то, что доктор прописал.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.