2013-04-23 2 views
3

Все, у меня проблема параллелизма с большим приложением, которое у меня недавно многопоточное. Проблема находится в процессоре сценария, который может запустить пакетных заданийКак сделать глобальный объект Thread-Safe

public async Task<Result> ProcessScriptAsync(
    CancellationTokenSource cancelSource, 
    TaskScheduler uiScheduler) 
{ 
    ... 
    // Get instance of active workbook on UI thread. 
    IWorkbook workbook = this.workbookView.ActiveWorkbook; 
    while (notFinished) 
    { 
     ... 
     Task<bool> runScriptAsyncTask = null; 
     runScriptAsyncTask = Task.Factory.StartNew<bool>(() => 
     { 
      return RunScript(ref workbook); 
     }, this.token, 
      TaskCreationOptions.LongRunning, 
      TaskScheduler.Default); 
     // Some cancellation support here... 

     // Run core asynchroniously. 
     try 
     { 
      bGenerationSuccess = await runScriptAsyncTask; 
     } 
     catch (OperationCanceledException) 
     { 
      // Handle cancellation. 
     } 
     finally 
     { 
      // Clean up. 
     } 
    } 
... 
} 

Моя проблема возникает, если учесть метод RunScript. Объект, передаваемый в RunScript, не является потокобезопасным и был создан в потоке пользовательского интерфейса. Таким образом, я должен создать «глубокую копию» этого объекта внутри метода RunScript ...

private bool RunScript(ref IWorkbook workbook) 
{ 
    ... 
    // Get a new 'shadow' workbook with which to operate on from a background thread. 
    IWorkbook shadowWorkbook; 
    if (File.Exists(workbook.FullName)) 
    { 
     // This opens a workbook from disk. The Factory.GetWorkbook method is thread safe. 
     shadowWorkbook = SpreadsheetGear.Factory.GetWorkbook(workbook.FullName); // (##) 
    } 
    else 
     throw new FileNotFoundException(
      "The current workbook is not saved to disk. This is a requirement. " + 
      "To facilitate multi-threading!"); 

    // Do hard work here... 
    shadowWorkbook.WorkbookSet.GetLock(); 
    try 
    { 
     // Do work and add worksheets to shadowWorkbook. 
    } 
    finally 
    { 
     // Reassign the UI workbook object to our new shadowWorkbook which 
     // has been worked on. This is fine to do as not work is actually being 
     // done on workbook. 
     workbook = shadowWorkbook; 
     shadowWorkbook.WorkbookSet.ReleaseLock(); 
    } 
} 

Моя проблема на линии, отмеченной (##). Каждый раз, когда выполняется RunScript, создается новый shadowWorkbook с диска. Проблема заключается в том, что некоторые рабочие листы создаются в shadowWorkbook, которые впоследствии копируются обратно в workbook в конце обработки. Однако каждый раз, когда я выполняю RunScript, я получаю книгу с диска, у которой нет новых листов, сгенерированных в последнем цикле.

Я задумал сделать shadowWorkbook глобальным объектом, но он создан в потоке пользовательского интерфейса и, следовательно, впоследствии не может использоваться из моей фоновой операции. Один из способов - сохранить книгу на диск после создания каждого рабочего листа, но есть много творений, и это будет дорого.

Есть ли способ сделать shadowWorkbook глобальной и потокобезопасной, что позволяет мне сохраняться изменения в моей IWorkbook через нить заклятий?

Спасибо за ваше время.

+0

Может быть [Неустойчивый] (http://msdn.microsoft.com/en-us/library/x13ttww7.aspx) является то, что вы ищете – noobob

+2

@noobob, возможно, нет. Что было бы «неустойчивым» в этой конкретной ситуации? И к какой области он должен применяться? –

+1

Вы хотите изменить файл из нескольких потоков одновременно? Боюсь, что нет простого способа сделать это. – svick

ответ

1

Хм, я бы сказал, что изменяемый ресурс в this.workbookView.ActiveWorkbook должен быть потокобезопасным в этой ситуации. Ваш объект рабочей книги, который вы говорите, создан в потоке пользовательского интерфейса, следовательно, вы получите разрешение на него там и в потоках задач при назначении workbook = shadowWorkbook.

Возможно, объявить объект синхронизации, например:

private static Object _objectLock = new Object(); 

и использовать как так в методе RunScript (и где-нибудь еще рабочая книга изменена), чтобы обеспечить последовательный доступ к ресурсу из различных потоков:

lock(_objectLock) 
{ 
    workbook.AddWorkSheet(); 
} 

В этом случае вам не нужно будет глубоко копировать свою книгу, поэтому удалите этот дорогой звонок и всю дополнительную сложность, которую он создает.

+0

+1 спасибо за идею, но это не сработает для меня. Когда вы говорите «изменяемый ресурс в этом. WorkbookView.ActiveWorkbook должен быть потокобезопасным в этой ситуации», то это будет отлично, но это не так, и это сторонняя библиотека. Назначение объекта «workbook» (объект, связанный с пользовательским интерфейсом) не является проблемой, поэтому желательно сохранение «shadowWorkbook» для повторного использования из фонового потока (без переключения контекста потока). – MoonKnight

1

Если вы правильно поняли, что вам нужно делать, то для каждого workbook создайте Thread. На этом Thread создайте свой shadowWorkbook, а затем запустите цикл, обрабатывающий запросы для этой книги, используя что-то вроде BlockingCollection<Action<IWorkbook>>.

Вы могли бы использовать, что, написав что-то вроде

workbookManager.Run(workbook, w => /* do work and add worksheets to w */); 
+0

Приветствия за идею. Операции, выполняемые на объектах «IWorkbook», должны быть последовательными; неясно, что этот подход также помог бы моей проблеме, и в каждом потоке у меня все еще была бы проблема, что мне нужно было бы создать объект «IWorkbook», когда каждый поток был запущен, и для этого в потоковом безопасном режиме потребовалось бы использование из 'shadowWorkbook = SpreadsheetGear.Factory.GetWorkbook (workbook.FullName);' который открывает экземпляр в соответствующем потоке - нужно продолжать использовать 'shadowWorkbook' между казнями. В настоящее время я добавил сохранение, чтобы обойти это ... – MoonKnight

+0

+1 за вашу помощь. Огромное спасибо. Думать об этом не похоже, что это будет возможно с использованием моего текущего дизайна ... – MoonKnight

Смежные вопросы