Есть ли потокобезопасный способ использования GhostScript через ImageMagick (. NET)? - PullRequest
0 голосов
/ 24 марта 2020

Я пишу API, используя ImageMagick. NET (отсюда: https://www.nuget.org/packages/Magick.NET-Q8-AnyCPU/) и GhostScript (отсюда: https://www.ghostscript.com/download.html), чтобы превратить PDF в миниатюра изображения:

        var thumbnailStream = new MemoryStream();
        using (var images = new MagickImageCollection())
        {
            // Read the frames into the collection
            resource.ResourceStream.Seek(0, SeekOrigin.Begin);
            var settings = new MagickReadSettings() { FrameIndex = 0, FrameCount = 1 };
            images.Read(resource.ResourceStream, settings); // WARNING - NOT THREAD SAFE!!

            // Take the first page and make a thumbnail out of it
            var image = images[0];

            // Scale thumbnail to proper width
            var geometry = new MagickGeometry(){ Width = ThumbnailWidth };
            image.Scale(geometry);

            // Write the thumbnail to a MemoryStream
            image.Write(thumbnailStream);
        }

Просто выполнение кода как есть с перерывами in:

  HResult=0x80131500
  Message=FailedToExecuteCommand `...gswin64c.exe" -q -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 "-sDEVICE=pngalpha" -dTextAlphaBits=4 -dGraphicsAlphaBits=4 "-r300x300" -dFirstPage=1 -dLastPage=1 "-sOutputFile=... 
(The system cannot find the file specified.) @ error/delegate.c/ExternalDelegateCommand/475

Это та же ошибка, что и в случае, если GhostScript isn ' т установлен вообще. Похоже, что исполняемый файл ghostscript (gswin64 c .exe) периодически недоступен . Однако, если я заверну вызов «Чтение» в блокировку следующим образом:

            private static readonly object ImageMagickReadLock = new object();

            // LOCK THE THREAD-UNSAFE CODE!!
            lock (ImageMagickReadLock)
            {
                images.Read(resource.ResourceStream, settings);
            }

... все работает, как и ожидалось! Однако это не масштабируемое решение (тестирование производительности нагрузки продемонстрировало нехватку ресурсов, которая может привести к тому, что этот процесс в среднем за 1,5 с достигнет более 20 с !!), поэтому я ищу поточно-безопасный способ использования GhostScript через ImageMagick ( . NET).

Здесь есть 7-летний поток здесь , который указывает, что можно получить исходный код для GhostScript и скомпилировать его самостоятельно с помощью специального флага. (Не знаю, почему безопасность потоков не используется по умолчанию, но это отдельное обсуждение). Однако я остался скептическим из-за возраста потока и, поскольку решение никогда не принималось - фактически, спрашивающий сообщил о проблемах с решением. Кроме того, я не верю, что пользователь использовал ImageMagick, поэтому я даже не уверен, что если бы я был , чтобы заставить его работать, даже если бы он мне даже помог.

Итак, я Задайте еще раз здесь: Существует ли потокобезопасный способ использования GhostScript через ImageMagick (. NET)?

Обновление:

После некоторого тестирования и помощи от dlemstra Я все еще вижу проблему, хотя она иная, чем прежде (что, я надеюсь, означает, что я ближе!).

Одной из моих проблем было то, что я использовал ghostscript .dll для обработки многопоточных запросов в неблокирующем случае, и когда поток, использующий .dll, занят, ImageMagick ищет .exe для заполнения с любыми последующими параллельными потоками. Это объясняет ошибку «файл не найден», которую я описал выше - даже если файл .dll присутствовал, необходимы как .dll, так и .exe ghostscipts. Поэтому я добавил .exe.

Тем не менее, я все еще получаю FailedToExecuteCommand, но на этот раз он может найти файл и подробности сказать error/ghostscript-private.h/InvokeGhostscriptDelegate/143.

Полное сообщение об ошибке :

ImageMagick.MagickDelegateErrorException
  HResult=0x80131500
  Message=FailedToExecuteCommand `".../GhostScript/gswin64c.exe" -q -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 "-sDEVICE=pngalpha" -dTextAlphaBits=4 -dGraphicsAlphaBits=4 "-r300x300" -dFirstPage=1 -dLastPage=1 "-sOutputFile=.../temp/magick-10252ZvAOzMMOsCXz%d" "-f...temp/magick-10252gRfuzynqR0Pe" "-f...temp/magick-10252Bp3xSsSeS1Ol"' (1) @ error/ghostscript-private.h/InvokeGhostscriptDelegate/143
  Source=Magick.NET-Q8-AnyCPU
  StackTrace:
   at ImageMagick.MagickImageCollection.NativeMagickImageCollection.ReadBlob(MagickSettings settings, Byte[] data, Int32 offset, Int32 length)
   at ImageMagick.MagickImageCollection.AddImages(Byte[] data, Int32 offset, Int32 count, MagickReadSettings readSettings, Boolean ping)
   at ImageMagick.MagickImageCollection.AddImages(Stream stream, MagickReadSettings readSettings, Boolean ping)
...