Многопоточность в Aspose.Slides для .NET

Введение

Хотя параллельная работа с презентациями возможна (за исключением парсинга/загрузки/клонирования) и обычно всё проходит гладко (в большинстве случаев), существует небольшая вероятность получения неправильных результатов при использовании библиотеки в нескольких потоках.

Мы настоятельно рекомендуем не использовать один экземпляр Presentation в многопоточной среде, поскольку это может привести к непредсказуемым ошибкам или сбоям, которые трудно обнаружить.

Загрузка, сохранение и/или клонирование экземпляра класса Presentation в нескольких потоках не безопасны. Такие операции не поддерживаются. Если необходимо выполнять такие задачи, следует параллелить операции, используя несколько одно‑поточных процессов — каждый из этих процессов должен использовать свой собственный экземпляр презентации.

Параллельное преобразование слайдов презентации в изображения

Допустим, мы хотим параллельно преобразовать все слайды PowerPoint‑презентации в изображения PNG. Поскольку использование одного экземпляра Presentation в нескольких потоках небезопасно, мы делим слайды презентации на отдельные презентации и конвертируем слайды в изображения параллельно, используя каждую презентацию в отдельном потоке. Ниже приведён пример кода, показывающий, как это сделать.

var inputFilePath = "sample.pptx";
var outputFilePathTemplate = "slide_{0}.png";
var imageScale = 2;

using var presentation = new Presentation(inputFilePath);

var slideCount = presentation.Slides.Count;
var slideSize = presentation.SlideSize.Size;

var conversionTasks = new List<Task>(slideCount);

for (var slideIndex = 0; slideIndex < slideCount; slideIndex++)
{
    // Извлечь слайд i в отдельную презентацию.
    var slidePresentation = new Presentation();
    slidePresentation.SlideSize.SetSize(slideSize.Width, slideSize.Height, SlideSizeScaleType.DoNotScale);
    slidePresentation.Slides.RemoveAt(0);
    slidePresentation.Slides.AddClone(presentation.Slides[slideIndex]);

    // Преобразовать слайд в изображение в отдельной задаче.
    var slideNumber = slideIndex + 1;
    conversionTasks.Add(Task.Run(() =>
    {
        try
        {
            var slide = slidePresentation.Slides[0];

            using var image = slide.GetImage(imageScale, imageScale);
            var imageFilePath = string.Format(outputFilePathTemplate, slideNumber);
            image.Save(imageFilePath, ImageFormat.Png);
        }
        finally
        {
            slidePresentation.Dispose();
        }
    }));
}

await Task.WhenAll(conversionTasks);

FAQ

Нужно ли вызывать настройку лицензии в каждом потоке?

Нет. Достаточно выполнить её один раз за процесс/домен приложения до запуска потоков. Если license setup может вызываться одновременно (например, при отложенной инициализации), синхронизируйте этот вызов, поскольку сам метод настройки лицензии не является потокобезопасным.

Могу ли я передавать объекты Presentation или Slide между потоками?

Передача «живых» объектов презентации между потоками не рекомендуется: используйте независимые экземпляры для каждого потока или предварительно создайте отдельные презентации/контейнеры слайдов для каждого потока. Такой подход соответствует общей рекомендации не делиться одним экземпляром презентации между потоками.

Безопасно ли параллельно экспортировать в разные форматы (PDF, HTML, изображения), если каждый поток имеет свой собственный экземпляр Presentation?

Да. При наличии независимых экземпляров и отдельных путей вывода такие задачи обычно корректно параллелятся; избегайте совместного использования объектов презентации и общих потоков ввода/вывода.

Что делать с глобальными настройками шрифтов (папки, замены) в многопоточной среде?

Инициализируйте все глобальные настройки шрифтов до запуска потоков и не меняйте их во время параллельной работы. Это устраняет гонки при доступе к общим ресурсам шрифтов.