使用async/await多线程处理大型C#应用程序

Moo*_*ght 7 c# multithreading task-parallel-library async-await .net-4.5

总而言之,我已经获得了多线程大型C#应用程序的工作.要做到这一点,我选择使用async/ await.我清楚地知道使用的IProgress<T>报告进度,以在UI(我们称之为"推"信息发送到UI),但我还需要从UI到"拉"的数据(在我的情况下的SpreadsheetGear工作簿,它包含数据).正是这种双向互动,我想要一些建议......

目前我触发了一个click事件来开始处理,代码具有以下结构:

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...
    ...
}
Run Code Online (Sandbox Code Playgroud)

然后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
    }
}
Run Code Online (Sandbox Code Playgroud)

到目前为止,这似乎很好.我现在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...
}
Run Code Online (Sandbox Code Playgroud)

在这种方法中,我需要从UI中提取数据并多次将数据写入UI.对于写作,我可以清楚地使用'progressInfo',但是如何处理来自UI的拉取信息.在这里,我使用了UI线程同步上下文,但这将完成很多次.有没有更好的方法来执行这些操作/我目前的方法有任何缺陷吗?

注意.显然,我会将Task.Factory.StartNew(...)代码包装成一个可重用的方法,上面的内容是明确显示的.

Ste*_*ary 2

如果您不断地在 UI 和线程池线程之间来回切换,您的代码将会有点混乱。

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

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