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

ADN Club => AutoCAD .NET API => Тема начата: Lemieux от 27-02-2021, 17:50:03

Название: Async/Await/Paralle
Отправлено: Lemieux от 27-02-2021, 17:50:03
Может и была тема, и я как всегда плохо искал, но поделюсь мыслями.

Понадобилось мне в 100500 файлах поменять веса линий в слоёв. Я написал код, который последовательно перебирает все файлы и делает, что надо. Потом я решил прикрутить async/await - заработало, потом решил прикрутить Parallel - заработало.
Для теста я взял примерно 20 папок, разной степени вложенности, и примерно 200 файлов. Результаты такие:
async/await - 23 секунды, интерфейс не блокируется
Paralle - 23 секунды, интерфейс блокируется
Стандартно - 30 секунд, интерфейс блокируется.
Процессор Xeon - 1230v3.
Самый лучший вариант async/await в данной ситуации, но мне непонятно почему такая небольшая разница относительно стандартного варианта. Вот код, может я что-то неправильно реализую. И ещё я не пробовал запускать несколько экземпляров AcCoreConsole.

Код - C# [Выбрать]
  1. using Autodesk.AutoCAD.DatabaseServices;
  2. using Autodesk.AutoCAD.Runtime;
  3. using Application = Autodesk.AutoCAD.ApplicationServices.Application;
  4. using System.IO;
  5. using System.Threading.Tasks;
  6. using System.Windows.Forms;
  7.  
  8. namespace LineWeightChange
  9. {
  10.     public class Commands
  11.     {
  12.         [CommandMethod("ASYNCMETHOD")]
  13.         public async void AsyncMethod()
  14.         {
  15.             string result = Browser();
  16.             if (result != "")
  17.             {
  18.                 bool taskResult = await Task.Run(() => AsyncWorkWithDirectories(result));
  19.                 if (taskResult)
  20.                     Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\nCOMPLETE\n");
  21.             }
  22.         }
  23.  
  24.         [CommandMethod("PARALLELMETHOD")]
  25.         public void ParallelMethod()
  26.         {
  27.             string result = Browser();
  28.             if (result != "")
  29.             {
  30.                 Parallel.Invoke(() => ParallelWorkWithDirectories(result));
  31.                 Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\nCOMPLETE\n");
  32.             }
  33.         }
  34.  
  35.         [CommandMethod("STANDARDMETHOD")]
  36.         public void StandardMethod()
  37.         {
  38.             string result = Browser();
  39.             if (result != "")
  40.             {
  41.                 StandardWorkWithDirectories(result);
  42.                 Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\nCOMPLETE\n");
  43.             }
  44.         }
  45.  
  46.         private void WorkWithDrawing(string path)
  47.         {
  48.             using (Database database = new Database(false, true))
  49.             {
  50.                 database.ReadDwgFile(path, FileOpenMode.OpenForReadAndWriteNoShare, true, null);
  51.                 using (Transaction tr = database.TransactionManager.StartTransaction())
  52.                 {
  53.                     LayerTable layerTable = tr.GetObject(database.LayerTableId, OpenMode.ForWrite) as LayerTable;
  54.                     if (layerTable.Has("SolidThick"))
  55.                     {
  56.                         LayerTableRecord layer = tr.GetObject(layerTable["SolidThick"], OpenMode.ForWrite) as LayerTableRecord;
  57.                         layer.LineWeight = LineWeight.LineWeight050;
  58.                     }
  59.                     if (layerTable.Has("SolidThin"))
  60.                     {
  61.                         LayerTableRecord layer = tr.GetObject(layerTable["SolidThin"], OpenMode.ForWrite) as LayerTableRecord;
  62.                         layer.LineWeight = LineWeight.ByLineWeightDefault;
  63.                     }
  64.                     tr.Commit();
  65.                 }
  66.                 SecurityParameters security = Application.DocumentManager.MdiActiveDocument.Database.SecurityParameters;
  67.                 database.SaveAs(path, true, DwgVersion.AC1024, security);
  68.             }
  69.         }
  70.  
  71.         private async Task<bool> AsyncWorkWithDirectories(string path)
  72.         {
  73.             bool result = true;
  74.             DirectoryInfo currentDirectory = new DirectoryInfo(path);
  75.             DirectoryInfo[] directories = currentDirectory.GetDirectories();
  76.             foreach (DirectoryInfo directory in directories)
  77.             {
  78.                 FileInfo[] files = directory.GetFiles();
  79.                 foreach (FileInfo file in files)
  80.                     if (file.Extension == ".dwg")
  81.                         await Task.Run(() => WorkWithDrawing(file.FullName));
  82.                 result = await Task.Run(() => AsyncWorkWithDirectories(directory.FullName));
  83.             }
  84.             return result;
  85.         }
  86.  
  87.         private void ParallelWorkWithDirectories(string path)
  88.         {
  89.             DirectoryInfo currentDirectory = new DirectoryInfo(path);
  90.             DirectoryInfo[] directories = currentDirectory.GetDirectories();
  91.             foreach (DirectoryInfo directory in directories)
  92.             {
  93.                 FileInfo[] files = directory.GetFiles();
  94.                 foreach (FileInfo file in files)
  95.                     if (file.Extension == ".dwg")
  96.                         Parallel.Invoke(() => WorkWithDrawing(file.FullName));
  97.                 Parallel.Invoke(() => ParallelWorkWithDirectories(directory.FullName));
  98.             }
  99.         }
  100.  
  101.         private void StandardWorkWithDirectories(string path)
  102.         {
  103.             DirectoryInfo currentDirectory = new DirectoryInfo(path);
  104.             DirectoryInfo[] directories = currentDirectory.GetDirectories();
  105.             foreach (DirectoryInfo directory in directories)
  106.             {
  107.                 FileInfo[] files = directory.GetFiles();
  108.                 foreach (FileInfo file in files)
  109.                     if (file.Extension == ".dwg")
  110.                         WorkWithDrawing(file.FullName);
  111.                 StandardWorkWithDirectories(directory.FullName);
  112.             }
  113.         }
  114.  
  115.         private string Browser()
  116.         {
  117.             FolderBrowserDialog browser = new FolderBrowserDialog
  118.             {
  119.                 Description = "Some description",
  120.                 ShowNewFolderButton = false
  121.             };
  122.             browser.ShowDialog();
  123.             return browser.SelectedPath;
  124.         }
  125.     }
  126. }
Название: Re: Async/Await/Paralle
Отправлено: Александр Ривилис от 27-02-2021, 17:56:36
но мне непонятно почему такая небольшая разница относительно стандартного варианта.
Потому что AutoCAD API однозадачный. И вообще всё что ты сделал крайне небезопасно. С AutoCAD API можно работать только из главной задачи AutoCAD.
Название: Re: Async/Await/Paralle
Отправлено: Lemieux от 27-02-2021, 18:05:42
С AutoCAD API можно работать только из главной задачи AutoCAD.
Ну, мне же никто не запрещает создавать бесконечное количество Database и работать с ними? Я не понимаю, что такого запретного в параллельной работе, AutoCAD не ломается, не ругается, выборочные чертежи нормально открываются и с ними можно работать.
Название: Re: Async/Await/Paralle
Отправлено: Александр Ривилис от 27-02-2021, 18:08:27
Lemieux,
Я всё сказал выше. Повторяться не буду.
Название: Re: Async/Await/Paralle
Отправлено: Lemieux от 27-02-2021, 18:09:46
Lemieux,
Я всё сказал выше. Повторяться не буду.
Я всё понял  ::)
Название: Re: Async/Await/Paralle
Отправлено: Привалов Дмитрий от 27-02-2021, 22:57:24
но мне непонятно почему такая небольшая разница относительно стандартного варианта. Вот код, может я что-то неправильно реализую.

решил прикрутить async/await - заработало, потом решил прикрутить Parallel - заработало.
Это ответ на твой же вопрос.
Прикрутить и знать+уметь пользоваться, это разные вещи!

Твой код не запускает параллельно несколько задач, как ты наверное ожидал. Он запускает выполнение одной задачи в другом потоке(ядре) и ждет.

Твой манипуляции с БД достаточно просты, я имею ввиду метод WorkWithDrawing.
Возможно в таком контексте это может работать параллельно основному потоку.
Можно ли запустить параллельно несколько методов WorkWithDrawing вопрос интересный, небольшой шанс есть.

Если ты попытаешься в метод WorkWithDrawing вставить Task.Run, либо попытаешься параллельно обращаться к одной и той же Database то вероятно обрушишь Автокад. Это люди уже пробовали, не получилось :-).

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