A multi-threaded large C # application using async / wait

That's it, I was entrusted with a multi-threaded large C # application. For this, I decided to use async / await . I am well aware of the use of IProgress<T> to report progress in the user interface (lets call it โ€œpushingโ€ information into the user interface), but I also need to โ€œpullโ€ the data from the user interface (in my case, this is a SpreadsheetGear workbook that contains data). This is a two-way interaction that I want to advise about ...

I am currently running a click event to start processing, and the code has the following structure:

 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... ... } 

Then in ProcessScriptAsync I have the following:

 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 } } 

That seems fine. The problem I currently have is the GenerateStage method, which now runs in the background thread of the thread stream

 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... } 

In this method, I need to extract data from the user interface and write data to the user interface many times. For writing, I can clearly use "progressInfo", but how to handle pulling information from the user interface. Here I used the UI thread synchronization context, but this will be done many times. Is there a better way to perform these operations / are there any flaws in my current approach?

Note. Obviously, I would return the Task.Factory.StartNew(...) code to the reusable method above is explicitly shown for security.

+7
source share
2 answers

If you constantly go back and forth between pool threads and a thread thread, your code will be a little dirty.

You essentially have two options: your โ€œnormalโ€ context will be a thread pool thread with parts scheduled for the user interface thread (as you have now), or your โ€œnormalโ€ context will be a user interface thread with parts scheduled for the user interface thread flow.

I usually prefer the latter, since you can use the simpler Task.Run instead of Task.Factory.StartNew for a specific TaskScheduler . But in any case, the code will be a little dirty.

+2
source

I did not work with the SpreadsheetGear workbook, but I believe that it has an event mechanism that you can use to store the relevant data for you in a user object that you can access from outside the UISynchronizationContext, so you avoid strict control restrictions over the user interface. This avoids blocking the user interface stream with

  Task task = Task.Factory.StartNew(() => { worksheet = workbookView.ActiveWorksheet; range = worksheet.UsedRange; }, CancellationToken.None, TaskCreationOptions.None, scheduler); try { task.Wait(); } finally { task.Dispose(); } 

from the GenerateStage method.

But then again, my suggestion is based on some assumptions about SpreadsheetGear workbook controls that might be wrong.

0
source

All Articles