THERE'S AN UPDATE BELOW THIS INITIAL QUESTION
I have a query that pulls in about 90,000 header records. I then want to iterate through that result set to get detail data for each header retrieved. If I process it linearly it take close to an hour and a half. If I parallelize it, I can get it done in 11 minutes. However, there's no screen updates. I have done multi-threaded applications lots of times, and have always been successful with doing things like:
this.lblStatus.Invoke(new MethodInvoker(() => this.lblStatus.Text = "Updating Standards Docs"));
However, this appears to really screen up a Parallel loop. Using that method for some screen updates, the Parallel loop never actually finished. So I need another method.
I've been trying:
Task.Factory.StartNew(() =>
{
OrderablePartitioner<PayrollHeader> partitioner = Partitioner.Create(query, EnumerablePartitionerOptions.NoBuffering);
Parallel.ForEach(partitioner, thisCheck =>
{
Interlocked.Increment(ref iChckNo);
lock (_status)
{
_status.ProcessMsg = "Voucher " + thisCheck.VoucherNumber;
_status.ProcessName = thisCheck.EmployeeName;
_status.CurrentRec = iChckNo;
dtSpan = DateTime.Now.Subtract(dtSpanStart);
_status.TimeMsg = string.Format("Elapsed {0}:{1}:{2}", dtSpan.Hours, dtSpan.Minutes, dtSpan.Seconds);
}
BeginInvoke((Action) (() =>
{
lblVoucher.Text = _status.ProcessMsg;
lblName.Text = _status.ProcessName;
lblCount.Text = string.Format("Record {0} of {1}", _status.CurrentRec, _status.TotalRecs);
lblTime.Text = _status.TimeMsg;
Application.DoEvents();
}));
thisCheck.GetDetails();
});
}).Wait();
The wait on the Task is because afterwards I need to do something else with the query afterwards, which I'll put into a ContinueWith statement eventually, I just really need to get the screen update to work.
I know all about cross thread corruption, which is why I'm trying to use the Invoker method... I firmly believe long running processes still need to keep the user informed, which is why I'm attempting this.
BTW, it's a WinForms app, not a WPF app. Any help at all would be greatly appreciated...
UPDATE:So someone wanted to see the updated code, with the IProgress into it.
Status _status = new Status {TotalRecs = query.Count};
var progress = new Progress<Status>(msg => WriteStatusUpdate(msg));
Task.Run(() =>
{
OrderablePartitioner<PayrollHeader> partitioner = Partitioner.Create(query, EnumerablePartitionerOptions.NoBuffering);
Parallel.ForEach(partitioner, thisCheck =>
{
lock (_status)
{
_status.ProcessMsg = "Voucher " + thisCheck.VoucherNumber;
_status.ProcessName = thisCheck.EmployeeName;
_status.CurrentRec = ++iChckNo;
dtSpan = DateTime.Now.Subtract(dtSpanStart);
_status.TimeMsg = string.Format("Elapsed {0}:{1}:{2}", dtSpan.Hours, dtSpan.Minutes, dtSpan.Seconds);
}
((IProgress<Status>) progress).Report(_status);
thisCheck.GetDetails();
});
}).Wait();
private void WriteStatusUpdate(Status _status)
{
lblVoucher.Text = _status.ProcessMsg;
lblVoucher.Refresh();
lblName.Text = _status.ProcessName;
lblName.Refresh();
lblCount.Text = string.Format("Records {0} of {1}", _status.CurrentRec, _status.TotalRecs);
lblCount.Refresh();
lblTime.Text = _status.TimeMsg;
lblTime.Refresh();
}
The code to update the screen never gets called...