大家好,我被委托将一个大型 C# 应用程序进行多线程处理。为此,我选择使用
目前,我通过触发点击事件来启动处理过程,代码结构如下:
在这种方法中,我需要多次从用户界面(UI)拉取数据并将数据写入UI。对于写入,我可以明确使用'progressInfo',但如何处理从UI提取信息。在这里,我使用了UI线程同步上下文,但这将会被多次执行。是否有更好的方法来执行这些操作/当前方法是否存在任何缺陷? 注意:显然我需要把
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(...)
的代码封装成可重用的方法,以上内容仅为简洁起见而显示。
WorkbookView
必须从 UI 线程访问,但IWorksheet
和IRange
可以从其他线程使用?你为什么不只是将这两个对象传递给GenerateStage()
呢? - svickworkbookViewAsync = workbookView
不会创建副本,它只是将引用复制到另一个变量。但新变量仍将指向旧对象。 - svickworksheet
或IRange
上执行操作,您必须在工作簿集或WorkbookView
上调用GetLock()
,这又意味着访问在UI线程上创建的WorkbookView
。 - MoonKnightGenerateStage
中访问workbookView
_WITHOUT_更改同步上下文 - 我不知道为什么会发生这种情况,如果我更新其他控件,我会得到通常的InvalidOperationException
??非常奇怪... - MoonKnight