2014-10-03 33 views
-1

在我的應用程序中,我正在創建報告。報告由並行運行的作業創建。報告需要存儲在獨特的目錄中。Synclock用於在多線程環境中創建獨特目錄

我的理解是,SyncLock塊中的代碼一次只能由一個線程調用。所以,我想出了下面的代碼從當前日期和時間,計數器產生一個唯一的路徑:

編輯: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當然不會鎖定任何東西,每個線程都有自己的成員副本。請記住,在多個線程上運行這種代碼是非常有害的。你只有一個磁盤,它不喜歡被多個線程統領。最終結果可能會比較慢,主要受磁盤尋道時間開銷的影響。 – 2014-10-03 11:51:46

+0

謝謝你的評論。報告的創建只是整個過程的一部分(接收工作,評估數據,創建報告,存儲結果)。每項工作一個接一個地執行單個任務。但是由於報告創建不是瓶頸,所以並行運行多個作業會有所幫助。我只有一個由所有作業使用的「ReportCreator」類實例。我將嘗試創建一個簡單的示例應用程序來複制行爲。 – Jens 2014-10-03 12:03:45

+0

您可以使用'Guid'作爲文件夾名稱,然後不需要同步。 – jmcilhinney 2014-10-03 12:04:22

回答

0

我在完全錯誤的結尾尋找錯誤。

我以爲鎖不工作但簡單的錯誤是

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

我重用級聯同一個變量。所以在進一步的循環迭代中,新的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。更好的是,使用提供滾動文件的日誌記錄庫(幾乎都可以)。 – 2014-10-03 12:49:57