c# - 使用 async/await 对大型 C# 应用程序进行多线程处理

标签 c# multithreading task-parallel-library .net-4.5 async-await

总而言之,我得到了对大型 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(...)编码成可重复使用的方法,为了简洁起见,上面的内容被明确显示。

最佳答案

如果您经常在 UI 和线程池线程之间来回切换,您的代码会有点困惑。

你基本上有两个选择:让你的“正常”上下文成为线程池线程,部分调度到 UI 线程(就像你现在拥有的那样),或者让你的“正常”上下文成为 UI 线程,部分调度到一个线程池线程。

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

关于c# - 使用 async/await 对大型 C# 应用程序进行多线程处理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14499295/

相关文章:

c# - 通过 IEnumerable 特征执行 Action<T>?

c# - .Net 中的多个 Parallel.ForEach 循环

c# - 将列表数据添加到文本文件

c# - 如何以编程方式遍历 GridView 的页面

python - 在 Python 的主线程中捕获线程的异常

c# - 等待非异步方法

c# - 如何使用 TPL 的 ActionBlock 写入文件?

loops - 循环中的异步和等待多个任务,如何返回单个任务

c# - ASP.net Razor - 使用 GMail SMTP 从网站发送电子邮件

c# - 基于处理器类型在 C# 应用程序中运行可变线程