2014-01-31 78 views
2

我有一個程序,基本上從數據庫中提取數據,將其緩存到一個文件,然後將該數據導出爲多種格式(Excel,Excel 2003,CSV)。我使用OpenXML SDK 2.0來完成Excel工作。這些導出過程並行運行(使用Parallel.ForEach),並且數據量可能非常大 - 例如一些CSV是800MB。在這些較大的導出期間,我注意到XML文檔的寫入會掛起。例如,如果我有8個並行輸出,在某些時候,他們只會「暫停」。他們都流連相同點:OpenXML在寫元素時掛起

//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

在這裏是表示所有的屏幕截圖(8在這種情況下)在阻斷或OpenXmlWriter.WriteElement方法內並行任務:爲這些掛起線程之一

Hung!

整個疊層 - 與註釋。

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個出口商正在運行,那麼可能有1000個文件也被寫入,但在任何給定時間只有8個文件正在寫入。

+0

你有沒有發現這個問題的解決?我現在遇到同樣的問題。 – syazdani

+0

不幸的是,沒有。我轉向其他事情。但這仍然是我真正需要解決的問題! –

回答

3

我知道這個問題很舊,但這是OpenXml-IsolatedFileStorage的Microsoft問題。您可以在這裏閱讀有關解決方法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 ... 
+0

有趣的,我會試試看。謝謝! –