7

所有,我一直在考虑的工作,多线程的大型C#应用程序。为此,我选择使用async/await。我深知使用IProgress<T>的报告进度的UI(我们称之为“推”信息发送到UI),但我还需要从UI到“拉”的数据(在我的情况下的SpreadsheetGear工作簿,其中包含数据)。这是我想了一些建议这种双向互动...多线程大型C#应用程序中使用异步/等待

目前我火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... 
    ... 
} 

然后在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(...)代码包装成可重复使用的方法,上面显示的是明智之举。

+1

所以,'WorkbookView'必须从UI线程访问,但是'IWorksheet'和'IRange'可以从另一个线程使用?你不能把这两个对象传给'GenerateStage()'吗? – svick

+1

'workbookViewAsync = workbookView'将*不*创建一个副本,它只会将*引用*复制到另一个变量中。但新变量仍然会指向旧对象。 – svick

+0

第一篇文章是一个合理的建议,但要在工作表或IRange上执行操作,您必须在工作簿集或WorkbookView上调用GetLock(),这又意味着要访问“WorkbookView”,它是在UI线程上创建。 – MoonKnight

回答

2

如果你不断地来回UI和线程池中的线程之间,你的代码将是一个有点凌乱。你基本上有两个选择:让你的“正常”上下文成为线程池线程,部分调度到UI线程(就像你现在这样做),或者让你的“正常”上下文成为部分调度的UI线程到一个线程池线程。

我通常更喜欢后者,因为您可以在一个特定的TaskScheduler上使用更简单的Task.Run而不是Task.Factory.StartNew。但无论如何,代码会有点混乱。

+0

我也不完全相信,我正在做的是最好的方法,但我真的不能看到另一个。代码很老很复杂 - 为了支持多线程而进行重新设计,不仅看起来很不理智(因为代码编写得很好),但在这种情况下,我不明白它对我有何帮助。我主要关心的是我为了从用户界面中提取数据而转离的“任务”数量。清楚地产生这些线程将有一个不可避免的开销 - 什么样的开销,我不知道呢......谢谢你的时间。 – MoonKnight

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控制一些假设,可以是不正确的。

+1

你可以看[文档](http://www.spreadsheetgear.com/support/help/spreadsheetgear.net.3.0/SpreadsheetGear~SpreadsheetGear.Windows.Forms.WorkbookView.html)来确认或反驳你的假设。 – svick