我有一个方法,将并行加载并运行报表布局。所有报告都将使用相同的baselayout.xml
。由于线程每次尝试访问同一资源时都会因异常而失败,因此我使用lock
来锁定文件。
public static XmlTextReader LoadReport(string reportName)
{
object _locker = new object();
object reportData;
lock (_locker)
{
reportData = Resources.ResourceManager.GetObject(reportName);
}
return new XmlTextReader(new MemoryStream((byte[])reportData));
}
并行方法如下所示:
private void RunReportsParallel(List<ReportObject> coverterList)
{
try
{
Parallel.ForEach(coverterList, (currentObject) => {
currentObject.Convert();
});
}
catch (Exception e)
{
smlLogger.Error(Helper.SetLogLine(e.Message, processId));
throw;
}
}
转换将运行以下代码:
public override SectionReport GetMainReport()
{
SectionReport mainReport = new SectionReport();
XMLDataSource datasource = new XMLDataSource(null, "//AkontoRechnung");
datasource.LoadXML(rechnungsdaten.ToString());
mainReport = new ReportAkontorechnung(datasource, reportConfiguration, Language, NoPrintOut);
try
{
mainReport.Run();
}
catch (Exception e)
{
smlLogger.Error(Helper.SetLogLine(string.Format("Error creating Report: {0}", e.Message), processId));
throw;
}
return mainReport;
}
在ReportAkontorechnung.cs
中抛出错误的行:
this.LoadLayout(Helper.LoadReport("ReportAkontoZusammenfassung"));
最后,错误:
bei GrapeCity.ActiveReports.Controls.Image.Load(Stream stream, Boolean checkMagic)
bei GrapeCity.ActiveReports.SectionReport.#Pyb(XmlNode node)
bei GrapeCity.ActiveReports.SectionReport.#Qyb(XmlDocument layoutDoc, Boolean checkNames)
bei GrapeCity.ActiveReports.SectionReport.LoadLayout(XmlReader reader, ArrayList& errors)
bei GrapeCity.ActiveReports.SectionReport.LoadLayout(XmlReader reader)
bei GFPrinting.Domain.ReportAkontorechnung.InitializeReport()
in C:\Dev\GFPrinting\Ruf.GFPrinting\branch\Concurrent\trunc\Source\SMLPrinting\Domain\ReportAkontorechnung.cs:Zeile 108.
bei GFPrinting.Domain.ReportAkontorechnung..ctor(XMLDataSource reportNavigation, ReportConfiguration reportConfiguration, String reportLanguage, Boolean noPrintout)
in C:\Dev\GFPrinting\Ruf.GFPrinting\branch\Concurrent\trunc\Source\SMLPrinting\Domain\ReportAkontorechnung.cs:Zeile 79.
bei GFPrinting.Domain.Akontorechnung.GetMainReport()
in C:\Dev\GFPrinting\Ruf.GFPrinting\branch\Concurrent\trunc\Source\SMLPrinting\Domain\Change\Akontorechnung.cs:Zeile 42.
bei GFPrinting.Domain.Change.ReportObject.Convert()
in C:\Dev\GFPrinting\Ruf.GFPrinting\branch\Concurrent\trunc\Source\SMLPrinting\Domain\Change\ReportObject.cs:Zeile 33.
bei GFPrinting.Domain.Rechnungshandler.<>c.<RunReportsParallel>b__13_0(ReportObject currentObject)
in C:\Dev\GFPrinting\Ruf.GFPrinting\branch\Concurrent\trunc\Source\SMLPrinting\Domain\Change\Rechnungshandler.cs:Zeile 103.
bei System.Threading.Tasks.Parallel.<>c__DisplayClass31_0`2.<ForEachWorker>b__0(Int32 i)
bei System.Threading.Tasks.Parallel.<>c__DisplayClass17_0`1.<ForWorker>b__1()
bei System.Threading.Tasks.Task.InnerInvoke()
bei System.Threading.Tasks.Task.InnerInvokeWithArg(Task childTask)
bei System.Threading.Tasks.Task.<>c__DisplayClass176_0.<ExecuteSelfReplicating>b__0(Object)
信息:
内部异常1:NullReferenceException:对象引用未设置为对象实例。对象引用未设置为对象实例。 (对象引用不指向对象实例。)
如何解决返回null
的问题?
编辑:
利亚姆的评论似乎解决了大多数问题。不使用并行加载,而是并行运行。我是在注意错误看到这样的选择。
您正在获取本地对象的锁定!将_locker声明为类中的私有静态对象(如果需要在实例中使用lock,请不要使用static。但是如果需要使用lock来处理此类的所有实例,则使用static。)
private static readonly object _locker = new object(); //readonly to avoid reassignment. static to lock on all instances.
然后把_locker
锁定为
lock(_locker)
{
}
虽然可以有办法进行无锁并行报告。
有点PLINQ,这将是非常严格的。从名称开始,按顺序将它们映射到资源,然后将资源并行映射到其处理的输出。
可能看起来有点像这样:
const string[] reportNames = { "ReportA", "ReportB" };
var results = reportNames
.AsSequential()
.Select
(
name => Resources.ResourceManager.GetObject(name)
)
.AsParallel()
.Select
(
reportData => DoSomethingCpuBound
(
new XmlTextReader(new MemoryStream((byte[])reportData))
)
)
.ToList();
这样,资源将按顺序加载,但处理并行进行。此外,此解决方案完全避免任何线程问题,因此不需要任何锁定。
注意:AsSequential()
调用并不是真正需要的(顺序是默认值),但我把它放在那里是为了说明目的。