簡體   English   中英

OpenXML在寫元素時掛起

[英]OpenXML hanging while writing elements

我有一個程序,它基本上從數據庫中提取數據,將其緩存到一個文件,然后將該數據導出為多種格式(Excel,Excel 2003,CSV)。 我正在使用OpenXML SDK 2.0來完成Excel工作。 這些導出過程是並行運行的(使用Parallel.ForEach ),並且數據量可能非常大 - 例如,某些CSV為800MB。 在這些較大的導出期間,我注意到XML文檔的編寫將會掛起。 例如,如果我有8個並行導出,那么在某些時候它們都會“暫停”。 它們都圍繞着同一點:

//this.Writer is an OpenXmlWriter which was created from a WorksheetPart.
this.Writer.WriteElement(new Cell()
{
    CellValue = new CellValue(value),
    DataType = CellValues.String
});

當發生這種情況時,我暫停調試器(在這種情況下是VS2013)並注意到所有線程都在代碼的相同部分阻塞 - 有些在OpenXML SDK中更深一些 - 但它們都源於對OpenXmlWriter.WriteElement的調用。

我使用JustDecompile挖掘了源代碼,但沒有找到任何答案。 似乎正在使用中間流寫入隔離存儲,這由於某種原因阻塞。 每個基礎流都是FileStream

這是一個屏幕截圖,顯示在OpenXmlWriter.WriteElement方法中或內部阻止的所有(在本例中為8個)並行任務:

紅!

其中一個掛起線程的完整堆棧 - 帶注釋。

WindowsBase.dll!MS.Internal.IO.Packaging.PackagingUtilities.CreateUserScopedIsolatedStorageFileStreamWithRandomName Normal
WindowsBase.dll!MS.Internal.IO.Packaging.PackagingUtilities.CreateUserScopedIsolatedStorageFileStreamWithRandomName(int retryCount, out string fileName)     
WindowsBase.dll!MS.Internal.IO.Packaging.SparseMemoryStream.EnsureIsolatedStoreStream()  

//---> Why are we writing to isolated storage at all?
WindowsBase.dll!MS.Internal.IO.Packaging.SparseMemoryStream.SwitchModeIfNecessary()  
WindowsBase.dll!MS.Internal.IO.Zip.ZipIOFileItemStream.Write(byte[] buffer, int offset, int count)   
System.dll!System.IO.Compression.DeflateStream.WriteDeflaterOutput(bool isAsync)     
System.dll!System.IO.Compression.DeflateStream.Write(byte[] array, int offset, int count)    
WindowsBase.dll!MS.Internal.IO.Packaging.CompressStream.Write(byte[] buffer, int offset, int count)  
WindowsBase.dll!MS.Internal.IO.Zip.ProgressiveCrcCalculatingStream.Write(byte[] buffer, int offset, int count)   
WindowsBase.dll!MS.Internal.IO.Zip.ZipIOModeEnforcingStream.Write(byte[] buffer, int offset, int count)  
System.Xml.dll!System.Xml.XmlUtf8RawTextWriter.FlushBuffer()     
System.Xml.dll!System.Xml.XmlUtf8RawTextWriter.WriteAttributeTextBlock(char* pSrc, char* pSrcEnd)    
System.Xml.dll!System.Xml.XmlUtf8RawTextWriter.WriteString(string text)  
System.Xml.dll!System.Xml.XmlWellFormedWriter.WriteString(string text)   
DocumentFormat.OpenXml.dll!DocumentFormat.OpenXml.OpenXmlElement.WriteAttributesTo(System.Xml.XmlWriter xmlWriter)   
DocumentFormat.OpenXml.dll!DocumentFormat.OpenXml.OpenXmlElement.WriteTo(System.Xml.XmlWriter xmlWriter)     
DocumentFormat.OpenXml.dll!DocumentFormat.OpenXml.OpenXmlPartWriter.WriteElement(DocumentFormat.OpenXml.OpenXmlElement elementObject)   

//---> At this point, threads seem to be blocking. 
MyProject.Common.dll!MyProject.Common.Export.ExcelWriter.WriteLine(string[] values) Line 117

值得一提的另一件事是,雖然有8件事(在這種情況下)被一次導出,但每個出口商都在寫入許多串聯的文件。 例如,給定的導出可能有150個要導出的基礎文件 - 輸入數據被分段,只有一部分寫入每個文件。 基本上,我從數據庫緩存批量數據,然后讀取一行並將其(按順序 - 逐個)推送到應包含此數據的流。 重點是,如果有8個出口商正在運行,那么可能有1,000個文件也被寫入,但只有8個在任何給定時間主動寫入。

我知道這個問題很老了,但這是微軟在OpenXml-IsolatedFileStorage中的問題。 您可以在http://support.microsoft.com/kb/951731上閱讀有關解決方法的信息:

IsolatedStorageFile類不是線程安全的,IsolatedStorageFile是靜態的,並且在所有PackagePart對象之間共享。 因此,當訪問使用IsolatedStorageFile對象來緩沖數據的多個PackagePart流進行寫入(包括刷新)時,IsolatedStorageFile類中的線程安全問題會暴露,從而導致死鎖。

基本思想是包裝一個PackagePart流並鎖定寫入它。 他們用包裹的流指出了一個例子。 這是實施:

public class PackagePartStream : Stream
{
    private readonly Stream _stream;

    private static readonly Mutex Mutex = new Mutex(false);

    public PackagePartStream(Stream stream)
    {
        _stream = stream;
    }

    public override long Seek(long offset, SeekOrigin origin)
    {
        return _stream.Seek(offset, origin);
    }

    public override void SetLength(long value)
    {
        _stream.SetLength(value);
    }

    public override int Read(byte[] buffer, int offset, int count)
    {
        return _stream.Read(buffer, offset, count);
    }

    public override void Write(byte[] buffer, int offset, int count)
    {
        Mutex.WaitOne(Timeout.Infinite, false);
        _stream.Write(buffer, offset, count);
        Mutex.ReleaseMutex();
    }

    public override bool CanRead
    {
        get { return _stream.CanRead; }
    }

    public override bool CanSeek
    {
        get { return _stream.CanSeek; }
    }

    public override bool CanWrite
    {
        get { return _stream.CanWrite; }
    }

    public override long Length
    {
        get { return _stream.Length; }
    }

    public override long Position
    {
        get { return _stream.Position; }
        set { _stream.Position = value; }
    }

    public override void Flush()
    {
        Mutex.WaitOne(Timeout.Infinite, false);
        _stream.Flush();
        Mutex.ReleaseMutex();
    }

    public override void Close()
    {
        _stream.Close();
    }

    protected override void Dispose(bool disposing)
    {
        _stream.Dispose();
    }
}

用法示例:

var worksheetPart = document.WorkbookPart.AddNewPart<WorksheetPart>();
var workSheetWriter = OpenXmlWriter.Create(new PackagePartStream(worksheetPart.GetStream()));
workSheetWriter.WriteStartElement(new Worksheet());
//rest of your code goes here ...

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM