If the backgroundworker will only be working for several seconds (not minutes), consider to convert your complete backgroundworker to async method.
If depends a bit, what your backgroundworker does: is it using a lot of processing power because of calculations, or is it mostly waiting idly for external processes to finish, like reading a database, fetching items from the internet, writing a file, etc.
In the later case, you'll see that the backgroundworker uses a lot of methods that will probably also have an async version.
If that is the case, consider to change your background work into an async method.
Suppose your background work is like this:
private async void BackgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
// get the input:
List<string> fileNames = (string)e.Argument;
List<int> processedProductIds = new List<int>();
// process the input
for (int i=0; i<fileNames.Count; ++i)
{
var products = ReadProductFile(fileNames[i]);
var productIds = AddProductsToDatabase(products);
processedProductIds.AddRange(productIds);
// report progress
var percentage = 100.0 * (double)i / (double)fileNames.Count;
backgroundWorker.ReportProgress(percentage, fileName);
}
// return output
e.Result = processedProductIds;
}
At its lowest level, the interface with the file reading and the database handling will have async methods. Consider to add async overloads for these method:
- Declare the method async
- Return
Task
instead of void
, return Task<TResult>
instead of TResult
- Only exception: async event handlers return void instead of Task
- Whenever you have to wait for another process, use the async method
- Continue doing what you must do, until you need the result of the async method
- To get the result of the async method, use
await
For example:
public async IReadOnlyCollection<Product> ReadProductFileAsync(string fileName)
{
List<Product> products = new List<Product>();
using (var textReader = File.OpenText(fileName))
{
string line = await textReader.ReadLineAsync();
while (line != null)
{
Product product = new Product(line);
products.Add(product);
line = await textReader.ReadLineAsync();
}
}
}
Change your DoWork to an async method. You don't have to use events to report progress:
public async Task<List<int>> DoWorkAsync(List<string> fileNames)
{
List<int> processedProductIds = new List<int>();
for (int i=0; i<fileNames.Count; ++i)
{
var products = await ReadProductFileAsync(fileNames[i]);
var productIds = await AddProductsToDatabaseAsync(products);
processedProductIds.AddRange(productIds);
// report progress
var percentage = 100.0 * (double)i / (double)fileNames.Count;
ReportProgress(percentage, fileName);
}
return processedProductIds;
}
ReportProgress will for instance update a progressBar and write the processed file:
void UpdateProgress (int percentage, string fileName)
{
this.progressBar.Value = percentage;
this.ListBoxFileNames.Items.Add(fileName);
}
All work is done by the same thread. Well, not exactly, but the thread after the await has the same context, so it can act as if it was the original thread. Therefore no need for mutexes, InvokeRequired, etc.
The event handler that would start your backgroundworker is now also async.
this.button1.Clicked += OnButtonStartProcessing_ClickedAsync;
private async void OnButtonStartProcessing_ClickedAsync(object sender, ...)
{
this.button1.Enabled = false;
this.progressBar1.Value = 0;
this.progressBar1.Visible = true;
List<string> fileNames = this.FetchFilesNamesToProcess();
List<int> processedIds = await ReadProductFilesAsync(fileNames);
this.UpdateProcessedIds(processedIds);
this.progressBar1.Visible = false;
this.button1.Enabled = true;
}
If you have to do some heavy calculations, so no wait for other process, use Task.Run
// Warning, this procedure will take 5 seconds!
private int Calculate(int x, int y) {...}
private async Task<int> CalculateAsync(int x, int y)
{
await Task.Run( () => this.Calculate(x, y));
}
Usage:
return await CalculateAsync(3, 4);
Finally: if you can do something useful instead of waiting for the other task to finish, don't await yet:
// Fetch the customer, do not await yet:
Task<int> taskA = FetchCustmer();
// because you didn't await, you can continue:
int b = DoSomethingElse();
// now you need the result for taskA
int a = await TaskA;
return a+b;