I have an windows form, where I can enter the number of records to create at once. If I mention to create 1 million records, it takes more time using a single thread using for loop. So I'm trying to create these 1 million records in a batch of 100/10 (configurable) using Parallel.ForEach
. While the operation is in progress, I'm trying to update the progress in the UI textbox (like showing many records completed). But the problem is, till before calling the Parallel.ForEach
progress status textbox is updating but within the Parallel.ForEach
operation, UI element is not updating. Once after finishing the Parallel.ForEach
only its updating.
public partial class CreateRecordsForm : Form
{
private void CreateRecordsBtn_Click(object sender, EventArgs e)
{
this.progressStatusTxt.Text = "Started Creating Records"; //Updating in UI
int count = 0;
await Task.Run(() => Parallel.ForEach(bulkCreateRequests, new ParallelOptions { MaxDegreeOfParallelism = parallelThreadsSizeInt }, request =>
{
dataservice.CreateRecords(request);
Interlocked.Increment(ref count);
string progressStatus = count " of " bulkCreateRequests.Count " Requests Completed";
this.progressStatusTxt.Text = progressStatus; //NOT updating in UI
}));
await Task.WhenAll();
this.progressStatusTxt.Text = "Finished Creating Records"; //Updating in UI
}
}
I have tried by creating a delegate to update the UI but still not working.
public delegate void BulkOrderCreateFormDelegate(string status);
//...
public BulkOrderCreateFormDelegate BulkOrderCreateFormCallback;
//...
BulkOrderCreate()
{
this.BulkOrderCreateFormCallback = new BulkOrderCreateFormDelegate(this.SetProgressStatusValueFn);
}
private void SetProgressStatusValueFn(string progressStatusValue)
{
if(!string.IsNullOrWhiteSpace(progressStatusValue))
this.progressStatusTxt.Text = progressStatusValue;
}
private void CreateRecordsBtn_Click(object sender, EventArgs e)
{
this.progressStatusTxt.Text = "Started Creating Records"; //Updating in UI
int count = 0;
await Task.Run(() => Parallel.ForEach(bulkCreateRequests, new ParallelOptions { MaxDegreeOfParallelism = parallelThreadsSizeInt }, request =>
{
dataservice.CreateRecords(request);
Interlocked.Increment(ref count);
string progressStatus = count " of " bulkCreateRequests.Count " Requests Completed";
BulkOrderCreateFormCallback(progressStatus); //Getting error : Cross thread operation
}));
await Task.WhenAll();
this.progressStatusTxt.Text = "Finished Creating Records"; //Updating in UI
}
Is anyone know how to do this? Can you refer me to a sample working code?
CodePudding user response:
For reporting progress you can use the Progress<T>
class and the IProgress<T>
interface. It is important to instantiate the Progress<T>
class on the UI thread, because it captures the current SynchronizationContext
during its instantiation, and uses the captured context to propagate the progress messages.
public partial class CreateRecordsForm : Form
{
private async void CreateRecordsBtn_Click(object sender, EventArgs e)
{
IProgress<string> progress = new Progress<string>(
message => this.progressStatusTxt.Text = message);
progress.Report("Started Creating Records");
int count = 0;
ParallelOptions options = new() { MaxDegreeOfParallelism = parallelThreadsSizeInt };
await Task.Run(() => Parallel.ForEach(bulkCreateRequests, options, request =>
{
dataservice.CreateRecords(request);
int localCount = Interlocked.Increment(ref count);
string progressStatus = localCount " of "
bulkCreateRequests.Count " Requests Completed";
progress.Report(progressStatus);
}));
progress.Report("Finished Creating Records");
}
}
The UI controls are affine to the UI thread. They are supposed to be manipulated exclusively on this thread.