【问题标题】:What is contaminating this switch statement?是什么污染了这个 switch 语句?
【发布时间】:2017-07-24 09:56:20
【问题描述】:

我有一个系统正在执行以下操作,

  • 将文档上传到 SharePoint
  • 事件接收器将添加作业到数据库,在文档转换目录中为作业创建一个文件夹
  • 目录观察器将触发 Document Conversion windows 服务
  • Windows 服务将从 DB 中批量获取 10 个作业(使用主线程)
  • 在启动时,Windows 服务会根据处理器的内核创建 X 个线程(使用 Parallel For)
  • 然后为每个数据库作业创建具有超时的工作线程(这与并行线程不同)
  • 它继续...
  • 哦,在转换时……在工作线程中……我们正在调用 ActiveDirectory,登录到 DB(读取、写入)并将文档上传回 SharePoint

我设法破解它...如果我上传受密码保护的文档...并且在上传 PowerPoint 文档后不久,PowerPoint 文档会抛出密码错误异常等。

但如果两个文档之间有 60 秒的间隔,一切正常,这意味着 powerpoint 文档确实可以转换为 PDF。

以下是代码,但我不得不从中删除不必要的部分,

这是事情开始的主要类,

 Parallel.For(0, noOfThreadsToRunOnPDFServer, new ParallelOptions { MaxDegreeOfParallelism = noOfThreadsToRunOnPDFServer },
    i =>
    {
        this.docConvService.ProcessDocuments(i);
    });

那么转换就在这里发生了……

using System;
using System.IO;
using System.Runtime.ExceptionServices;
using System.Threading;

namespace PDFService
{
    public class AsposePDFConverter : IPDFConverter
    {
        private IDocConversionSettings settings;
        private ExceptionDispatchInfo conversionException = null;
        public enum SupportedExtensions
        {
            Doc,
            Docx,
            Xls,
            Xlsx,
            Pdf,
            Pps,
            Ppsx,
            Ppt,
            Pptx,
            Txt,
            Html,
            Mhtml,
            Xhtml,
            Msg,
            Eml,
            Emlx,
            One,
            Vsd,
            Vsdx,
            Vss,
            Vssx
        }

        public AsposePDFConverter(IDocConversionSettings settings)
        {
            this.settings = settings;
        }

        private void SyncThreadStartWithTimeout(ThreadStart threadStart, TimeSpan timeout)
        {
            Thread workerThread = new Thread(threadStart);
            workerThread.Start();

            bool finished = workerThread.Join(timeout);
            if (!finished)
            {
                workerThread.Abort();
                throw new ConversionTimeoutException("PDF Conversion exceeded timeout value");
            }
        }

        public MemoryStream ConvertToPDF(string documentName, Stream docContent, double timeoutMS)
        {
            this.conversionException = null;

            MemoryStream outStream = null;
            MemoryStream inStream = new MemoryStream();
            docContent.CopyTo(inStream);
            inStream.Seek(0, SeekOrigin.Begin);

            SupportedExtensions documentExtension;
            string szExtension = Path.GetExtension(documentName).TrimStart('.');
            if (Enum.TryParse(szExtension, true, out documentExtension))
            {
                switch (documentExtension)
                {
                    case SupportedExtensions.Doc:
                    case SupportedExtensions.Docx:
                    case SupportedExtensions.Txt:
                    case SupportedExtensions.Html:
                    case SupportedExtensions.Mhtml:
                    case SupportedExtensions.Xhtml:
                        SyncThreadStartWithTimeout(
                                () => { outStream = ConvertWordsToPDF(inStream); },
                                TimeSpan.FromMilliseconds(timeoutMS));
                        break;
                    case SupportedExtensions.Pps:
                    case SupportedExtensions.Ppsx:
                    case SupportedExtensions.Ppt:
                    case SupportedExtensions.Pptx:
                        SyncThreadStartWithTimeout(
                                () => { outStream = ConvertSlidesToPDF(inStream); },
                                TimeSpan.FromMilliseconds(timeoutMS));
                        break;
                }

                // Conversion happens on sub-threads so they can time out, if they throw an exception, throw it from this thread
                if (this.conversionException != null)
                    this.conversionException.Throw();

                return outStream;
            }
            else
            {
                throw new FormatNotSupportedException("Document type is not supported");
            }
        }

        private MemoryStream ConvertWordsToPDF(Stream docContent)
        {
            try
            {
                Aspose.Words.License lic = new Aspose.Words.License();
                lic.SetLicense(this.settings.AsposeLicensePath);
                Aspose.Words.Document doc = new Aspose.Words.Document(docContent);

                MemoryStream stream = new MemoryStream();
                doc.Save(stream, Aspose.Words.SaveFormat.Pdf);
                return stream;
            }
            catch (Exception ex)
            {
                this.conversionException = ExceptionDispatchInfo.Capture(ex);
                return null;
            }
        }

        private MemoryStream ConvertSlidesToPDF(Stream docContent)
        {
            try
            { 
                Aspose.Slides.License lic = new Aspose.Slides.License();
                lic.SetLicense(this.settings.AsposeLicensePath);
                using (Aspose.Slides.Presentation presentation = new Aspose.Slides.Presentation(docContent))
                {
                    MemoryStream stream = new MemoryStream();
                    presentation.Save(stream, Aspose.Slides.Export.SaveFormat.Pdf);
                    return stream;
                }
            }
            catch (Exception ex)
            {
                this.conversionException = ExceptionDispatchInfo.Capture(ex);
                return null;
            }
        }

    }
}

错误是,

文档 PDF 转换期间出错。详细信息是:PDFConversionID: 6061,文档名称:powerpoint.ppsx,WebURL:已删除,上传者: 已移除,转换持续时间:00:01:06.3072410

Aspose.Words.IncorrectPasswordException: 文档密码是 不正确。在 Aspose.Words.Document。 (流,加载选项)
在 Aspose.Words.Document。 (流,加载选项)在 DocumentPDFConversionService.AsposePDFConverter.ConvertWordsToPDF(Stream docContent) 在...中

如您所见,发生了一些非常可疑的事情

【问题讨论】:

  • 如果您将 Parallel.For 循环更改为标准 For 循环,问题是否仍然存在?这将帮助您诊断是否存在线程安全问题。

标签: c# multithreading windows-services task-parallel-library aspose


【解决方案1】:

您在多个线程中使用 this.docConvService 的同一实例,因此您的 conversionException 属性可能是由受密码保护的文档在处理其他文档时写入的。您应该实例化 AsposePDFConverter 的新实例,或更改返回异常的方式,例如在 ConvertToPDF 返回的结果对象中,其中包含 MemoryStream 和您的错误。

每个请求的单独实例:

Parallel.For(0, noOfThreadsToRunOnPDFServer, new ParallelOptions { MaxDegreeOfParallelism = noOfThreadsToRunOnPDFServer },
    i =>
    {
        new AsposePdfConverter(settings).ProcessDocuments(i);
    });

返回一个结果对象:

 public ConversionResult ConvertToPDF(string documentName, Stream docContent, double timeoutMS)
{
    /** Your code **/
    return new ConversionResult() 
        {
        MemoryStream = memoryStream,
        ConversionException = conversionException
        };
}

class ConversionResult {
    MemoryStream MemoryStream {get;set;}
    ExceptionDispatchInfo ConversionException {get;set;}
}

【讨论】:

  • 谢谢,除此之外,我正在考虑将文档转换转移到完全不同的服务,atm 我们的 windows 服务正在转换文档,读写到数据库和 SharePoint 并调用活动目录 + 记录到文件系统。当您查看它时,您会建议任何仅用于文档转换的架构:)
  • @Mathematics Aspose.Words API 确实支持多线程,唯一需要注意的是,一次只能有一个线程访问 Document Object。例如,您可以编写一个方法,其中初始化 Document 的新实例并将输出保存在全局 List 对象中,该对象可以在执行结束后访问,以获取所有输出流/文件。您可以按照上述指南,根据您的任务要求来实现您的多线程例程。我在 Aspose 担任支持开发人员。
  • @AsadAli 我可以按照给定的指南解决问题,但是我们可以快速聊天吗?我想讨论一些不应该花费太多时间的事情 :)
  • @Mathematics 你肯定会随时问,我相信你也在 Aspose 论坛上发布了类似的询问。虽然你可能会在这里问,但在论坛上,我相信我们可以相应地回复你更多的细节和有用的信息。 :)
猜你喜欢
  • 1970-01-01
  • 2019-12-11
  • 2013-07-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-30
相关资源
最近更新 更多