2014-10-03 3 views
-1

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

Это мое понимание, что код в блоке SyncLock может быть вызван только по одному потоку за раз. Поэтому я придумал следующий код для генерации уникального пути от текущей даты и времени и счетчика:

Edit: DirectoryLock определяется как Private DirectoryLock As Object = New Object

'Create a non existant path 
Dim ID As String 'Unique ID, that is added to the path 
Dim RandomReportName As String 'The resulting directory name 
SyncLock DirectoryLock 'Lock the creation of the directory, to avoid the same names 
    Dim number As Integer = 0 
    Do 
     number += 1 
     ID = Format(number, "000") 'Create ID from a counter 
     RandomReportName = "Report_" & Format(Date.Now, "yyyy_MM_dd-HH_mm_ss.fff") & "_(" & ID & ")" 'Generate the path 
     opts.OutputPath = IO.Path.Combine(opts.OutputPath, RandomReportName) 
    Loop Until IO.Directory.Exists(opts.OutputPath) = False 'Increase the counter, until a non existant path is found 
    IO.Directory.CreateDirectory(opts.OutputPath) 'Create the directory in the synclock, so other threads will need to find a new name (higher counter) 
End SyncLock 'Free the code 

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

Что мне не хватает (как должно быть что-то :-)) и как можно избежать такой проблемы?

Ответы как на VB.NET, так и на C# очень приветствуются.

+0

отлична от нуля вероятность того, что «DirectoryLock» должен быть 'Shared'. Мы не можем видеть из фрагмента, будут ли потоки создавать новый объект класса, частью которого является этот код. В этом случае DirectoryLock ничего не блокирует, каждый поток имеет свою собственную копию члена. Имейте в виду, что запуск такого кода более чем в одном потоке очень вреден. У вас есть только один диск, он не любит, когда его управляют несколькими потоками. Конечный результат может быть * lot * медленнее, в котором доминируют временные затраты времени на поиск диска. –

+0

Благодарим вас за комментарий. Создание отчета является частью всего процесса (получение задания, оценка данных, создание отчета, сохранение результатов). Отдельные задачи выполняются один за другим на одно задание. Но поскольку создание отчета не является узким местом, полезно параллельно выполнять несколько заданий. У меня есть только один экземпляр класса ReportCreator, который используется всеми заданиями. Я попытаюсь создать простое примерное приложение для репликации поведения. – Jens

+0

Вы можете просто использовать «Guid» для имени папки, и тогда синхронизация не потребуется. – jmcilhinney

ответ

0

Я искал совершенно неправильный конец ошибки.

Я думал, что замок не работает, однако простая ошибка была

opts.OutputPath = IO.Path.Combine(opts.OutputPath, RandomReportName) 

повторно использовать ту же переменную для конкатенации. Таким образом, на последующих итерациях цикла новое RandomReportName добавляется к уже существующему.

Решение, конечно, является использование временной переменной

'Create a non existant path 
Dim ID As String 
Dim RandomReportName As String 
Dim ResultPath As String = "" 'Stores the unique path name 
SyncLock DirectoryLock 
    Dim number As Integer = 0 
    Do 
     number += 1 
     ID = Format(number, "000") 'Create ID from a counter 
     RandomReportName = "Report_" & Format(Date.Now, "yyyy_MM_dd-HH_mm_ss.fff") & "_(" & ID & ")" 'Generate the path 
     ResultPath = IO.Path.Combine(opts.OutputPath, RandomReportName) 'Assign the result path with the combined base path and unique name 
    Loop Until IO.Directory.Exists(ResultPath) = False 
    IO.Directory.CreateDirectory(ResultPath) 
    opts.OutputPath = ResultPath 'Store the temporary variable for further use 
End SyncLock 'Free the code 
+0

Вместо того чтобы пытаться заблокировать код кода генерации всего кода, когда все, что вам нужно, это уникальный номер, используйте Interlocked.Increment. Еще лучше, используйте библиотеку протоколирования, которая обеспечивает скользящие файлы (почти все). –

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