Обработка долгих процессов в потоке

Автор Тема: Обработка долгих процессов в потоке  (Прочитано 8086 раз)

0 Пользователей и 1 Гость просматривают эту тему.

Тема содержит сообщение с Решением. Нажмите здесь чтобы посмотреть его.

Оффлайн Алексей (IdeaSoft)Автор темы

  • ADN
  • *
  • Сообщений: 1188
  • Карма: 9
    • idea-soft.ru
  • Skype: makar_govorun
Подскажите. К примеру нужно решить долговременную задачу для обработки примитивов.
Решил сделать обработку подпрограммы в потоке.
И вот столкнулся с такой проблемой, когда подпрограмма, которая решает задачу
выполняется в потоке, то не возможно сделать изменения в базе данных.
Возникает исключение "eLockVialetion".
Как обойти эту проблему?

Вот код тестовой команды:
Код - vb.net [Выбрать]
  1.         <CommandMethod("TEST")> _
  2.         Public Shared Sub COMM_TEST()
  3.  
  4.             Dim db As Database = acadnet_document_GetDatabase() ' Эта функция у меня возвращает объект базы работает хорошо
  5.             Dim FrmProc = New FrmProgress  ' Создаю экземпляр формы процесса с ProgressBar и кнопка "отмена"  
  6.  
  7.             FrmProc.db = db   ' ссылка на базу записываю в поле формы  
  8.             FrmProc.Show()     ' показываю форму
  9.             FrmProc.CollectionObj = ... ' тут к примеру будем получать список идентификаторов примитивов (но он просто заведомо у меня есть)
  10.  
  11.             FrmProc.Start()      ' Стартую поток
  12.  
  13.         End Sub

Вот код формы демонстрирующий ход процесса

Код - vb.net [Выбрать]
  1. Public Class FrmProgress
  2.     Public Ext As Boolean = False
  3.     Public db As Database            ' ссылка на базу
  4.     Public CollectionObj as List(Of String) ' Коллекция Handle примитивов
  5.     ' Создание и старт потока
  6.     Public Sub Start()
  7.         Dim Tr As New System.Threading.Thread(AddressOf EXECUTE)
  8.         Tr.Start()
  9.     End Sub
  10.     ' Команда отмены
  11.     Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
  12.         Ext = True
  13.         Me.Close()
  14.     End Sub
  15.     ' Команда реализующая задуманное (к примеру поменять цвет у примитивов)
  16.     Public Sub EXECUTE()
  17.         Me.txtProc.Text = "Подождите, пока программа поменяет цвета примитивам..."
  18.         Me.txtProc.Invalidate()
  19.         Me.barProcess.Maximum = 10
  20.         Dim i As Integer = 0
  21.         Dim dec As Integer = Me.CollectionObj.Count \ 10 ' будем менять процесс по 10%
  22.         Dim kp As Integer = 0
  23.         For Each H As String In Me.CollectionObj
  24.             If (i = kp * dec) Then
  25.                 If kp <= Me.barProcess.Maximum Then
  26.                     Me.barProcess.Value = kp
  27.                     Me.Invalidate()
  28.                     kp += 1
  29.                 End If
  30.             End If
  31.             If Me.Ext Then Exit Sub ' досрочный выход по кнопке "Отмена"
  32.             acadnet_Primitive_SetColor(H, 6, Me.db) ' окрашиваем примитив в розовый цвет = 6
  33.             i += 1
  34.         Next
  35.         Me.Button1.Text = "Закрыть"
  36.     End Sub
  37. End Class

И вот код подпрограммы меняющий цвет примитиву

Код - vb.net [Выбрать]
  1.     Public Shared Sub acadnet_Primitive_SetColor(ByVal H As String, _
  2.                                                  ByVal cVal As Integer, _
  3.                                                  ByRef db As Database)
  4.             Using trans As Transaction = db.TransactionManager.StartTransaction
  5.                 Try
  6.                     Dim ln As Long = Convert.ToInt64(H, 16)
  7.                     Dim Hn = New Handle(ln)
  8.                     Dim ID = db.GetObjectId(False, Hn, 0)
  9.                     Dim dbObj = trans.GetObject(ID, OpenMode.ForWrite, False, True)
  10.                     Dim ent = DirectCast(dbObj, Entity)
  11.                     If (ent IsNot Nothing) Then
  12.                         ent.UpgradeOpen()
  13.                         ent.ColorIndex = cVal
  14.                     End If
  15.                 Catch ex As System.Exception
  16.                     ' тут вот eLockVialation
  17.                 End Try
  18.                 trans.Commit()
  19.             End Using
  20.    End Sub


   

Оффлайн Александр Пекшев aka Modis

  • ADN Club
  • *****
  • Сообщений: 1658
  • Карма: 366
  • Отец modplus.org
    • ModPlus
eLockVialetion обычно значит, что нужно использовать doc.LockDocument()

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13830
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Решил сделать обработку подпрограммы в потоке.
Неправильно решил. AutoCAD непотокобезопасный, поэтому обращатся к базе AutoCAD из потока нельзя - только из главного потока.
Возникает исключение "eLockVialetion".
Значит базу нужно блокировать:
Код - vb.net [Выбрать]
  1. Using lockdocc As DocumentLock = Application.DocumentManager.MdiActiveDocument.LockDocument()
  2. ' Здесь код работы с базой
  3. End Using
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Отмечено как Решение Алексей (IdeaSoft) 21-12-2016, 08:54:32

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13830
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Алексей (IdeaSoft)Автор темы

  • ADN
  • *
  • Сообщений: 1188
  • Карма: 9
    • idea-soft.ru
  • Skype: makar_govorun
eLockVialetion обычно значит, что нужно использовать doc.LockDocument()
Через doc.LockDocument() тоже делал - вообще фатальная ошибка.
Если делать не через поток а на прямую, то и без doc.LockDocument() все работает.
Проблемы, только когда делаешь через поток.

Оффлайн Алексей (IdeaSoft)Автор темы

  • ADN
  • *
  • Сообщений: 1188
  • Карма: 9
    • idea-soft.ru
  • Skype: makar_govorun
Использование потоков (Thread) для фоновой обработки
Ага! сейчас посмотрю...

Оффлайн Александр Пекшев aka Modis

  • ADN Club
  • *****
  • Сообщений: 1658
  • Карма: 366
  • Отец modplus.org
    • ModPlus
Судя по примеру в топике - вообще не нужно никаких потоков. Просто делаете немодальное окно, делаете ему Topmost = true и пусть себе меняет цвет отрезков на розовый ))

Оффлайн Алексей (IdeaSoft)Автор темы

  • ADN
  • *
  • Сообщений: 1188
  • Карма: 9
    • idea-soft.ru
  • Skype: makar_govorun
Использование потоков (Thread) для фоновой обработки
Я так понял, что нужно делать процедуру как delegate.


Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13830
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Я так понял, что нужно делать процедуру как delegate.
Не это главное. Главное, что функция работы с базой из Thread должна запускаться через Control.Invoke()
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Алексей (IdeaSoft)Автор темы

  • ADN
  • *
  • Сообщений: 1188
  • Карма: 9
    • idea-soft.ru
  • Skype: makar_govorun
Судя по примеру в топике - вообще не нужно никаких потоков.

Нет не так. Изменение цвета это как пример для постановки проблемы.
Проблема именно в том что поток нужен.

Поток нужен именно потому что, если пользователю надоело ждать завершения
долгого процесса он мог нажать кнопку "отмена" не дожидаясь пока дорожка на форме закончится.
А без потока этого не получится, пока не завершится цикл.

Оффлайн Алексей (IdeaSoft)Автор темы

  • ADN
  • *
  • Сообщений: 1188
  • Карма: 9
    • idea-soft.ru
  • Skype: makar_govorun
Не это главное. Главное,
А тогда понятно буду пробовать.
Заранее спасибо.

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13830
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Алексей (IdeaSoft)Автор темы

  • ADN
  • *
  • Сообщений: 1188
  • Карма: 9
    • idea-soft.ru
  • Skype: makar_govorun
Использование потоков (Thread) для фоновой обработки

Работает!
Посмотрите, я модифицировал этот код
на примере создания 100 000 отрезков.
Может где косяк есть?

namespace BackgroundProcess {
Код - C# [Выбрать]
  1.     public class Commands : IExtensionApplication {
  2.         int N = 100000; // буду создавать сто тысяч отрезков
  3.         static FrmProc MyFrm;
  4.         public void Initialize()  {
  5.             MyFrm = new FrmProc();
  6.             MyFrm.CreateControl();
  7.         }
  8.         public void Terminate() {
  9.         }
  10.         void BackgroundProcess() {
  11.             for (int i=0; i<N; i++) {
  12.                 if (MyFrm.Ext) break;
  13.                 MyFrm.label1.Text = "Создано " + i.ToString() + " отрезков";
  14.                 MyFrm.ProgBar.Value = i;
  15.                 MyFrm.Invalidate();
  16.                 if (MyFrm.InvokeRequired)
  17.                     MyFrm.Invoke(new FinishedProcessingDelegate(FinishedProcessing));
  18.                 else
  19.                     FinishedProcessing();
  20.             }
  21.             MyFrm.Ext = false; // сброс флага досрочного выхода  
  22.             MyFrm.Hide();      // Скрываем форму процесса
  23.         }
  24.         delegate void FinishedProcessingDelegate();
  25.         void FinishedProcessing() {
  26.                 Document doc = acApp.DocumentManager.MdiActiveDocument;
  27.                 using (doc.LockDocument()) {
  28.                     using (Transaction tr = doc.Database.TransactionManager.StartTransaction())  {
  29.                         BlockTable bt =  (BlockTable)tr.GetObject(doc.Database.BlockTableId, OpenMode.ForRead);
  30.                         BlockTableRecord ms = (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite);
  31.                         Line line = new Line(new Point3d(0, 0, 0), new Point3d(10, 10, 0));
  32.                         ms.AppendEntity(line);
  33.                         tr.AddNewlyCreatedDBObject(line, true);
  34.                         tr.Commit();
  35.                     }
  36.                 }
  37.         }
  38.         [CommandMethod("TEST")]
  39.         public void ProcessBackground() {
  40.             System.Threading.Thread thread = new Thread(new ThreadStart(BackgroundProcess));
  41.             MyFrm.ProgBar.Maximum = N;
  42.             MyFrm.ProgBar.Value = 0;
  43.             MyFrm.Show();  // Показываю диалог демонстрации процесса
  44.             thread.Start();
  45.         }
  46.     }
  47. }

Код формы процесса
Код - C# [Выбрать]
  1. namespace psm_cfunc {
  2.     public partial class FrmProc : Form {
  3.         public bool Ext = false;
  4.         public FrmProc() {
  5.             InitializeComponent();
  6.         }
  7.         // обработчик события для кнопки "Отмена"
  8.         private void button1_Click(object sender, EventArgs e) {
  9.             Ext = true;
  10.        }
  11.     }
  12. }
  13.  







Только не пойму видеоролик как добавлять.



« Последнее редактирование: 20-12-2016, 19:58:08 от Алексей (IdeaSoft) »

Оффлайн Алексей (IdeaSoft)Автор темы

  • ADN
  • *
  • Сообщений: 1188
  • Карма: 9
    • idea-soft.ru
  • Skype: makar_govorun
http://adndevblog.typepad.com/autocad/2012/07/allowing-users-to-escape-from-long-operations-in-autocad-net.html
http://adn-cis.org/kak-pozvolit-polzovatelyu-prervat-dlitelnuyu-operacziyu-v-autocad-.net.html
Да точно можно попробовать сделать через DoEvents
Я уже и забыл про этот способ.
Делал через DoEvents аж в 2003 году.



Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13830
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Только не пойму видеоролик как добавлять.
Читай у меня в подписи: Создание и добавление Autodesk Screencast видео в сообщение на форуме
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение