3

全体として、最近マルチスレッド化した大規模なアプリケーションで同時実行の問題が発生しました。問題は、バッチ ジョブを実行できるスクリプト プロセッサ内にあります。

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コピーされることです。workbookただし、実行するたびRunScriptに、最後のループで生成された新しいシートを持たないディスクからワークブックを取得します。

グローバル オブジェクトの作成を検討しshadowWorkbookましたが、UI スレッドで作成されるため、バックグラウンド操作から使用できません。これを回避する 1 つの方法は、ワークシートを作成するたびにワークブックをディスクに保存することですが、作成数が多く、コストが高くなります。

shadowWorkbookグローバルスレッド セーフにする方法はありIWorkbookますか?

御時間ありがとうございます。

4

2 に答える 2

1

私があなたを正しく理解していれば、あなたがすべきことは、それぞれworkbookに対してThread. そのThread上で を作成し、shadowWorkbookのようなものを使用して、そのワークブックのリクエストを処理するループを実行しますBlockingCollection<Action<IWorkbook>>

あなたはそれを次のように書くことで使用します

workbookManager.Run(workbook, w => /* do work and add worksheets to w */);
于 2013-04-23T14:00:47.257 に答える
1

this.workbookView.ActiveWorkbookうーん、この状況では可変リソースはスレッドセーフである必要があると言えます。あなたが言うワークブックオブジェクトはUIスレッドで作成されるため、割り当て時にそことタスクスレッドで競合が発生しますworkbook = shadowWorkbook

おそらく、同期オブジェクトを宣言します。たとえば、次のようになります。

private static Object _objectLock = new Object();

RunScript メソッド (およびワークブックが変更された他の場所) でそのように使用して、異なるスレッドからリソースへのシリアル アクセスを保証します。

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

この場合、ワークブックをディープ コピーする必要がないため、高価な呼び出しと、それによって生じる余分な複雑さをすべて取り除きます。

于 2013-04-23T08:34:58.493 に答える