Как избежать нуля с оператором блокировки? - PullRequest
0 голосов
/ 05 апреля 2019

У меня есть метод, который будет параллельно загружать и запускать макет отчета. Все отчеты будут использовать одинаковые 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;
    }
}

Conver выполнит следующий код:

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: Der Objektverweis wurde их ничто не мешает объекту. Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt. (Ссылка на объект не указывает на экземпляр объекта.)

Как мне решить проблему возврата null?

Редактировать :
Комментарий Лиама, кажется, решает большинство проблем. Не используя параллельную загрузку, но параллельный запуск. Я должен был зафиксировать ошибку, чтобы увидеть такую ​​опцию.

Ответы [ 2 ]

5 голосов
/ 05 апреля 2019

Вы приобретаете блокировку на локальном объекте!Объявите _locker как закрытый статический объект в классе (если для работы экземпляра требуется блокировка, не используйте static. Но если блокировка требуется для работы со всеми экземплярами этого класса, используйте static.)

private static readonly object _locker = new object(); //readonly to avoid reassignment. static to lock on all instances.

А затем установите блокировку на _locker как

lock(_locker)
{

}

Хотя могут быть способы создания параллельных отчетов без блокировок.

0 голосов
/ 05 апреля 2019

Это будет очень striaghtforward с небольшим количеством 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() на самом деле не нужен (по умолчанию - последовательный), но я поставил его там для наглядности.

...