Сообщество программистов Autodesk в СНГ

ADN Club => AutoCAD .NET API => Тема начата: Андрей Миков от 05-05-2020, 20:40:31

Название: Транзакция в новом потоке
Отправлено: Андрей Миков от 05-05-2020, 20:40:31
Здравствуйте. У меня в программе запускается длительная операция, в которой выполняются многочисленные обращения к БД Автокада для получения и создания объектов. Чтобы не блокировать все приложение, я запустил ее в отдельном таске. При этом если в процессе расчета какая-то транзакция завершается без вызова Commit(), то есть при явном или неявном вызове Abort(), то выбрасывается исключение: System.AccessViolationException: 'Попытка чтения или записи в защищенную память. Это часто свидетельствует о том, что другая память повреждена.'. Пример кода который его инициирует:
Код - C# [Выбрать]
  1. Task.Run(() =>
  2. {
  3.      var trans = bd.TransactionManager.StartTransaction();
  4.      trans.Abort();
  5.      trans.Dispose();
  6. });
Причем исключение возникает в основном UI потоке Автокада, и приложение завершается. Если во всех транзакциях выполнен вызов Commit(), то регулярной ошибки нет, но тем не менее время от времени она возникает в разных местах, то есть проблема не ушла. Подскажите, возможно вообще работать с базой Автокада в нескольких потоках, и если да, то как правильно это делать?
Название: Re: Транзакция в новом потоке
Отправлено: Александр Пекшев aka Modis от 05-05-2020, 20:52:47
Попробуйте так:
1. При запуске плагина (до инициализации окон) положите в статическую переменную текущий Dispatcher
2. Внутри вашей длительной задачи все обращения к БД автокада выполняйте внутри этого Dispatcher.
Что-то типа того:
Код - C# [Выбрать]
  1. public void Invoke(Action doAction)
  2. {
  3.     try
  4.     {
  5.         if (doAction != null)
  6.         {
  7.             _currentDispatcher?.Invoke(doAction);
  8.         }
  9.     }
  10.     catch (Exception exception)
  11.     {
  12.         // отображение, логирование или еще что
  13.     }
  14. }
  15. ...
  16.  
  17. _mainThreadEvent.Invoke(() =>
  18. {
  19.     // работа с БД
  20. });
  21.  
Название: Re: Транзакция в новом потоке
Отправлено: Александр Ривилис от 05-05-2020, 22:22:25
Подскажите, возможно вообще работать с базой Автокада в нескольких потоках, и если да, то как правильно это делать?
Нельзя. Только из главного потока. Точка.
Название: Re: Транзакция в новом потоке
Отправлено: Александр Ривилис от 05-05-2020, 22:27:46
https://adn-cis.org/ispolzovanie-potokov-dlya-fonovoj-obrabotki.html
Название: Re: Транзакция в новом потоке
Отправлено: Андрей Миков от 06-05-2020, 13:42:00
Александр Пекшев aka Modis, спасибо, в Dispatcher основного потока все методы отлично работают.
https://adn-cis.org/ispolzovanie-potokov-dlya-fonovoj-obrabotki.html
Пример интересный, но создание контрола для запуска процесса в основном потоке похоже на хак. Может быть сделать пример с Dispatcher? И использовать таск вместо создания потока руками.
Еще есть вопрос - можно ли во вторичных потоках создавать объекты автокада, или через конструктор, или через вызов методов Clone, Offset и др, а затем в основном потоке открыть транзакцию и добавить их в базу? Это корректно?
Название: Re: Транзакция в новом потоке
Отправлено: Александр Ривилис от 06-05-2020, 13:47:26
создание контрола для запуска процесса в основном потоке похоже на хак.
Это рекомендация от ADN DevHelp. Так что использование допустимо.
Еще есть вопрос - можно ли во вторичных потоках создавать объекты автокада, или через конструктор, или через вызов методов Clone, Offset и др, а затем в основном потоке открыть транзакцию и добавить их в базу? Это корректно?
Нет. Это всё чревато спонтанными аварийными завершениями AutoCAD...
Название: Re: Транзакция в новом потоке
Отправлено: Привалов Дмитрий от 06-05-2020, 15:20:34
У меня в программе запускается длительная операция, в которой выполняются многочисленные обращения к БД Автокада для получения и создания объектов. Чтобы не блокировать все приложение, я запустил ее в отдельном таске.

1. Для изменения БД, тебе в любом случае придется блокировать документ.
2. Даже если подумать логически, не стоит давать пользователю делать что-то во время изменения БД, т.к. пользователь под этим может подразумевать открытие документов, переключение на другие документы, удаление объектов. Пусть ждет.
3. Autocad использует ядро ACIS (https://en.wikipedia.org/wiki/ACIS)
Скорее всего, разработчики ядра не предусмотрели параллельную работу из различных потоков. Упоминание об ограничении есть в документации Autodesk. На форумах были попытки работы с API из различных потоках, но ни к чему не привели. Не имеет смысла тратить время, чтобы убедиться, что это не работает.

Если операция длительная
Вставь progressbar.
https://through-the-interface.typepad.com/through_the_interface/2007/05/displaying_a_pr.html
Можешь добавить возможность прервать процесс. (Если очень долгий процесс.)
https://adn-cis.org/kak-pozvolit-polzovatelyu-prervat-dlitelnuyu-operacziyu-v-autocad-.net.html
Название: Re: Транзакция в новом потоке
Отправлено: Владимир Шу от 06-05-2020, 17:28:55
Я бы подумал про изменение логики работы программы.
Когда программа работает долго, то для оптимизации я стараюсь разделить "бизнес логику" и вывод результата работы. Обсчитывать "бизнес логику", построенную на своих объектах и классах можно в многопоточном режиме и потом собрав все в кучу вывести результат одним потоком.
Сам по себе вывод большего количества примитивов или изменение примитивов происходит быстро, ну не миллионами же Вы их меняете. Может и Вам стоит сначала собрать и подготовить все данные, а потом пакетом вывести в файл.
Название: Re: Транзакция в новом потоке
Отправлено: Андрей Миков от 06-05-2020, 18:03:12
Даже если подумать логически, не стоит давать пользователю делать что-то во время изменения БД
Да, есть риск что пользователь что-то удалит и весь расчет упадет  :(
Скорее всего, разработчики ядра не предусмотрели параллельную работу из различных потоков
Сейчас, когда даже в телефонах стоят многоядерные процессоры, это особенно печалит...
Вставь progressbar.
Это именно то что я собирался сделать. Спасибо за пример, я и не знал что есть готовый прогрессбар - ProgressMeter. Правда очень уж куцый он, ни процент вывести, ни оставшееся время. И почему-то затирается строкой состояния при обновлении экрана. Есть какой то ProgressTrayItem, может быть по нему есть пример, не нашел его в документации.
Я бы подумал про изменение логики работы программы.
В том то и дело что бизнес логика содержит создание объектов автокада - как промежуточных, для расчета, так и окончательных для добавления в базу. И разделить их не получится.

Может быть действительно многопоточность в автокаде это плохая идея. Запустить в одном потоке, заморозить все приложение, и пусть ждут.
Название: Re: Транзакция в новом потоке
Отправлено: Привалов Дмитрий от 06-05-2020, 18:25:22
Правда очень уж куцый он, ни процент вывести, ни оставшееся время.
Про процент не понял, т.к. именно процент он и выводит, плюс заголовок.
Если у тебя несколько последовательных действий, то можешь вновь создать его с новым заголовком, так повысишь информативность:
pm.Start("Подготовка данных");
....
pm.Start("Расчет");
...
pm.Start("Вывод результата");
..может и от обновления экрана поможет.


ну и все как в примере, сначала pm.Start(), затем pm.SetLimit(499);  т.к. pm.Start() сбросит лимит.
Название: Re: Транзакция в новом потоке
Отправлено: Владимир Шу от 06-05-2020, 18:34:04
бизнес логика содержит создание объектов автокада - как промежуточных, для расчета,
Вот от этого я и предлагаю отказаться. Вы видимо пытаетесь что то решить геометрическими построениями, что влечет за собой огромные накладные расходы, может стоит посмотреть и поискать аналитические решения, это точно будет быстрее и можно будет параллелить.
Название: Re: Транзакция в новом потоке
Отправлено: Андрей Миков от 06-05-2020, 19:18:30
Про процент не понял, т.к. именно процент он и выводит, плюс заголовок.
я имел ввиду вывод процента в виде числа, плюс вывод оставшегося времени выполнения, этапа
Если у тебя несколько последовательных действий, то можешь вновь создать его с новым заголовком, так повысишь информативность:
и заголовок не меняется, даже при UpdateScreen, только созданием нового прогрессбара можно новый установить
Вот от этого я и предлагаю отказаться.
да, использование другого решения снимет эту проблему, особенно принимая во внимание случаи некорректной работы методов автокада. Останавливает то что на поиск и использование нового решения потребуется время....
Название: Re: Транзакция в новом потоке
Отправлено: Александр Ривилис от 06-05-2020, 20:23:10
Цитата: Привалов Дмитрий от 06-05-2020, 18:25:22

    Про процент не понял, т.к. именно процент он и выводит, плюс заголовок.

я имел ввиду вывод процента в виде числа, плюс вывод оставшегося времени выполнения, этапа
Цитата: Привалов Дмитрий от 06-05-2020, 18:25:22

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

и заголовок не меняется, даже при UpdateScreen, только созданием нового прогрессбара можно новый установить
Это стандартный Progressbar, который использует сам AutoCAD. Если тебя не устраивает стандартный, то можешь создать свой (например, немодальная форма).

Останавливает то что на поиск и использование нового решения потребуется время....
Вполне возможно, что это решение уже найдено и тебе на форуме подскажут. Главное не стесняться задавать вопросы.
Название: Re: Транзакция в новом потоке
Отправлено: Андрей Миков от 06-05-2020, 21:25:44
Хорошо. Спасибо за помощь!
Название: Re: Транзакция в новом потоке
Отправлено: Андрей Миков от 06-05-2020, 21:51:21
Александр, еще вопрос: вообще, безотносительно потоков, возможно создание объектов автокада вне транзакции? с последующим добавлением их в транзакцию или освобождением через Dispose. Или это тоже чревато?
Название: Re: Транзакция в новом потоке
Отправлено: Александр Ривилис от 06-05-2020, 22:21:44
возможно создание объектов автокада вне транзакции?
Да. Это в принципе нормальный сценарий. Более того ведь есть вариант вообще не использовать транзакции.
Название: Re: Транзакция в новом потоке
Отправлено: Александр Ривилис от 06-05-2020, 22:22:46
еще вопрос
У нас принято "один вопрос - одна тема". Это на будущее.
Название: Re: Транзакция в новом потоке
Отправлено: Андрей Миков от 07-05-2020, 07:44:29
Ок. Спасибо!
Название: Re: Транзакция в новом потоке
Отправлено: Привалов Дмитрий от 07-05-2020, 10:28:08
возможно создание объектов автокада вне транзакции? с последующим добавлением их в транзакцию или освобождением через Dispose. Или это тоже чревато?
В теории можно попробовать создавать объекты в другом потоке и добавлять в массив. Только брать простые объекты, типа точек и отрезков, а не таблицы и размеры.
Затем в основном потоке добавлять их в нужное пространство, закрывать транзакцией и удалять из массива.
Такой сценарий может и сработает, но вероятно не даст существенного прироста по скорости.