7

总而言之,我被赋予了对大型 C# 应用程序进行多线程处理的工作。为此,我选择使用async/ await。我很清楚使用IProgress<T>UI报告进度(让我们将此称为向 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 和线程池线程之间来回切换,你的代码会有点混乱。

你基本上有两个选择:让你的“正常”上下文是线程池线程,其中部分计划到 UI 线程(就像你现在拥有的那样),或者让你的“正常”上下文是 UI 线程,部分计划到线程池线。

我通常更喜欢后者,因为您可以使用更简单的Task.Run而不是Task.Factory.StartNew特定的TaskScheduler. 但无论哪种方式,代码都会有点混乱。

于 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 回答