Hi,
I have recently upgraded from 0.65 to 0.73 and while I have seen some performance improvements, several reports that run in parallel threads crashed in my UAT environment. It seems the problem is in the AdjustToContents() method.
The full exception is:
---> System.InvalidOperationException: Object is currently in use elsewhere.
at System.Drawing.Graphics.MeasureString(String text, Font font, SizeF layoutArea, StringFormat stringFormat)
at System.Drawing.Graphics.MeasureString(String text, Font font, Int32 width, StringFormat format)
at ClosedXML.Excel.FontBaseExtensions.GetWidth(IXLFontBase fontBase, String text, Dictionary`2 fontCache)
at ClosedXML.Excel.XLColumn.AdjustToContents(Int32 startRow, Int32 endRow, Double minWidth, Double maxWidth)
at ClosedXML.Excel.XLColumns.<AdjustToContents>b__8(XLColumn c)
at System.Collections.Generic.List`1.ForEach(Action`1 action)
at ClosedXML.Excel.XLColumns.AdjustToContents()
Comments: Not sure if locking is the right way here, the methods GetWidth and GetHeight are called many times throughout the AdjustToContents function. Locking is not a fast process. I would recommend passing a graphics object down from the AdjustToContents method to GetWidth and GetHeight and get rid of the static object. This will be thread-safe and get rid of the locking. While you are at it, two further thread-safety issues in the Extensions class. 1.IntegerExtensions.ToStringLookup: - A normal Dictionary is not threadsafe, especially not writing to it, either lock it or use ConcurrentDictionary. I'm not sure about the dictionary there at all, it might get really huge very fast, considering that you try to cache each and every integer you pass into it. I'm not sure about the performance gain by this lookup. If it is really that much faster, cache the lookup locally in the method you need it in. 2. StringExtensions.FixNewLines - Regex object is not threadsafe, simply use the static Regex.Replace/Match etc. methods and .NET will automatically cache the Regex objects for it and make sure it's thread-safe. In that case you should not use the RegexOptions.Compiled option.
I have recently upgraded from 0.65 to 0.73 and while I have seen some performance improvements, several reports that run in parallel threads crashed in my UAT environment. It seems the problem is in the AdjustToContents() method.
The full exception is:
---> System.InvalidOperationException: Object is currently in use elsewhere.
at System.Drawing.Graphics.MeasureString(String text, Font font, SizeF layoutArea, StringFormat stringFormat)
at System.Drawing.Graphics.MeasureString(String text, Font font, Int32 width, StringFormat format)
at ClosedXML.Excel.FontBaseExtensions.GetWidth(IXLFontBase fontBase, String text, Dictionary`2 fontCache)
at ClosedXML.Excel.XLColumn.AdjustToContents(Int32 startRow, Int32 endRow, Double minWidth, Double maxWidth)
at ClosedXML.Excel.XLColumns.<AdjustToContents>b__8(XLColumn c)
at System.Collections.Generic.List`1.ForEach(Action`1 action)
at ClosedXML.Excel.XLColumns.AdjustToContents()
Comments: Not sure if locking is the right way here, the methods GetWidth and GetHeight are called many times throughout the AdjustToContents function. Locking is not a fast process. I would recommend passing a graphics object down from the AdjustToContents method to GetWidth and GetHeight and get rid of the static object. This will be thread-safe and get rid of the locking. While you are at it, two further thread-safety issues in the Extensions class. 1.IntegerExtensions.ToStringLookup: - A normal Dictionary is not threadsafe, especially not writing to it, either lock it or use ConcurrentDictionary. I'm not sure about the dictionary there at all, it might get really huge very fast, considering that you try to cache each and every integer you pass into it. I'm not sure about the performance gain by this lookup. If it is really that much faster, cache the lookup locally in the method you need it in. 2. StringExtensions.FixNewLines - Regex object is not threadsafe, simply use the static Regex.Replace/Match etc. methods and .NET will automatically cache the Regex objects for it and make sure it's thread-safe. In that case you should not use the RegexOptions.Compiled option.