[英]How to Make a Global Object 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
的對象不是線程安全的,而是在UI線程上創建的。 因此,我必須在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
。 這樣做的問題是在shadowWorkbook
中創建了一些工作表,這些工作表隨后在處理結束時被復制回workbook
。 但是,每次執行RunScript
我都會從磁盤獲取工作簿,而該工作簿沒有在最后一個循環中生成新工作表。
我已經研究過使shadowWorkbook
成為一個全局對象,但它是在UI線程上創建的,因此無法在后台操作中使用。 解決此問題的一種方法是在每個工作表創建后將工作簿保存到磁盤,但是有很多創建,這將是昂貴的。
有沒有辦法使shadowWorkbook
全局和線程安全,使我能夠跨線程調用持久更改我的IWorkbook
?
謝謝你的時間。
嗯,我想說在這種情況下, this.workbookView.ActiveWorkbook
中的可變資源需要是線程安全的。 您說的工作簿對象是在UI線程上創建的,因此當您分配workbook = shadowWorkbook
時,您將在那里和任務線程中爭用它。
也許聲明一個同步對象,例如:
private static Object _objectLock = new Object();
並在RunScript方法(以及修改工作簿的任何其他位置)中使用,以確保從不同的線程對資源進行串行訪問:
lock(_objectLock)
{
workbook.AddWorkSheet();
}
在這種情況下,您不需要深度復制工作簿,因此刪除了昂貴的調用以及它創建的所有額外復雜性。
如果我理解正確,你應該做的是為每個workbook
創建一個Thread
。 在該Thread
,創建您的shadowWorkbook
,然后運行一個循環來處理該工作簿的請求,使用BlockingCollection<Action<IWorkbook>>
。
您可以通過編寫類似的東西來使用它
workbookManager.Run(workbook, w => /* do work and add worksheets to w */);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.