簡體   English   中英

如何使全局對象線程安全

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM