7

すべて、私は大規模なC#アプリケーションをマルチスレッド化する仕事を与えられました。これを行うために、私はasync/を使用することを選択しましたawaitを使用して進行状況をIProgress<T>UIに報告することをよく知っています(これを「プッシュ」情報と呼びましょう)が、UIからデータを「プル」する必要もあります(私の場合は、データを含むSpreadsheetGearワークブック) )。私がアドバイスを求めているのは、この双方向の相互作用です...

現在、クリックイベントを発生させて処理を開始していますが、コードの構造は次のとおりです。

CancellationTokenSource cancelSource;
private async void SomeButton_Click(object sender, EventArgs e)
{
    // Set up progress reporting.
    IProgress<CostEngine.ProgressInfo> progressIndicator =
        new Progress<CostEngine.ProgressInfo>();

    // Set up cancellation support, and UI scheduler.
    cancelSource = new CancellationTokenSource();
    CancellationToken token = cancelSource.Token;
    TaskScheduler UIScheduler = TaskScheduler.FromCurrentSynchronizationContext();

    // Run the script processor async.
    CostEngine.ScriptProcessor script = new CostEngine.ScriptProcessor(this);
    await script.ProcessScriptAsync(doc, progressIndicator, token, UIScheduler);

    // Do stuff in continuation...
    ...
}

次に、でProcessScriptAsync、私は次のようになります:

public async Task ProcessScriptAsync(
    SpreadsheetGear.Windows.Forms.WorkbookView workbookView, 
    IProgress<ProgressInfo> progressInfo,
    CancellationToken token, 
    TaskScheduler UIScheduler)
{
    // This is still on the UI thread.
    // Here do some checks on the script workbook on the UI thread.
    try
    {
        workbookView.GetLock();
        // Now perform tests...
    }
    finally { workbookView.ReleaseLock(); }

    // Set the main processor off on a background thread-pool thread using await.
    Task<bool> generateStageTask = null;
    generateStageTask = Task.Factory.StartNew<bool>(() => 
        GenerateStage(workbookView, 
            progressInfo, 
            token, 
            UIScheduler));
    bool bGenerationSuccess = await generateStageTask;

    // Automatic continuation back on UI thread.
    if (!bGenerationSuccess) { // Do stuff... }
    else {
     // Do other stuff
    }
}

これまでのところ、これは問題ないようです。私が今抱えている問題はGenerateStage、バックグラウンドスレッドプールスレッドで実行されるメソッドにあります

private bool GenerateStage(
    SpreadsheetGear.WorkbookView workbookView, 
    IProgress<ProgressInfo> progressInfo, 
    CancellationToken token, 
    TaskScheduler scheduler)
{
    ...
    // Get the required data using the relevant synchronisation context.
    SpreadsheetGear.IWorksheet worksheet = null;
    SpreadsheetGear.IRange range = null;
    Task task = Task.Factory.StartNew(() =>
    {
        worksheet = workbookView.ActiveWorksheet;
        range = worksheet.UsedRange;
    }, CancellationToken.None,
       TaskCreationOptions.None,
       scheduler);
    try
    {
        task.Wait();
    }
    finally
    {
        task.Dispose();
    }

    // Now perform operations with 'worksheet'/'range' on the thread-pool thread...
}

この方法では、UIからデータをプルし、UIに何度もデータを書き込む必要があります。執筆には「progressInfo」を明確に使用できますが、UIからのプル情報の処理方法。ここでは、UIスレッド同期コンテキストを使用しましたが、これは何度も実行されます。これらの操作を実行するためのより良い方法はありますか/私の現在のアプローチに欠陥はありますか?

ノート。明らかに私はTask.Factory.StartNew(...)コードを再利用可能なメソッドにまとめます。上記は簡潔さのために明示的に示されています。

4

2 に答える 2

2

UIスレッドとスレッドプールスレッドの間を絶えず行き来している場合、コードは少し厄介になります。

基本的に2つのオプションがあります。「通常の」コンテキストをUIスレッドにスケジュールされた部分を持つスレッドプールスレッドにするか、「通常の」コンテキストをスレッドプールにスケジュールされた部分を持つUIスレッドにします。スレッド。

特定Task.Runのの代わりに単純なものを使用できるので、私は通常後者を好みます。しかし、いずれにしても、コードは少し厄介になります。Task.Factory.StartNewTaskScheduler

于 2013-01-24T16:59:30.987 に答える
0

SpreadsheetGearブックは使用しませんでしたが、UISynchronizationContextの外部からアクセスできるカスタムオブジェクトに関連データを格納するために使用できるイベントメカニズムがあると思います。これにより、回避できます。ブックのUIコントロールに強くバインドされています。これにより、UIスレッドのブロックを回避できます。

 Task task = Task.Factory.StartNew(() =>
{
    worksheet = workbookView.ActiveWorksheet;
    range = worksheet.UsedRange;
}, CancellationToken.None,
   TaskCreationOptions.None,
   scheduler);
try
{
    task.Wait();
}
finally
{
    task.Dispose();
}

GenerateStageメソッドの一部です。

しかし、繰り返しになりますが、私の提案は、SpreadsheetGearブックのコントロールに関するいくつかの仮定に基づいていますが、これは正しくない可能性があります。

于 2013-01-24T11:37:37.890 に答える