Want to use a parallel task library with progress reports to update the database

I developed an application that stores several connection strings. I just loop through the for loop and concatenate each db and execute sql against each db. this way I am updating several databases using a massive sql statement.

Now I need to use the parallel task library to update several db at the same time instead of updating one at a time in one cycle. I also want to handle the exception, and also want to show several progress indicators for several database operations. pause and resume work should be there.

when I click on the button, then several db connections will open, and a new progress bar will be added for each task in my form. each progress bar shows each progress of the db operation. when any task is completed, the corresponding progress bar will be removed from the form.

anyone can help me with code example how to do this using TPL. here I have one code that updates one progress indicator, but I need to update several progress indicators. int iterations = 100;

ProgressBar pb = new ProgressBar(); pb.Maximum = iterations; pb.Dock = DockStyle.Fill; Controls.Add(pb); Task.Create(delegate { Parallel.For(0, iterations, i => { Thread.SpinWait(50000000); // do work here BeginInvoke((Action)delegate { pb.Value++; }); }); }); 

UPDATE Question

I did it this way. the code works, but all the values โ€‹โ€‹of the execution line increase sequentially. I have one form and one user control in winform applications. please look at my code and tell me what is wrong there.

main for code

 public partial class Main : Form { public Main() { InitializeComponent(); this.DoubleBuffered = true; } private void btnStart_Click(object sender, EventArgs e) { Progress ucProgress = null; Dictionary<string, string> dicList = new Dictionary<string, string>(); dicList.Add("GB", "conn1"); dicList.Add("US", "conn2"); dicList.Add("DE", "conn3"); fpPanel.Controls.Clear(); Task.Factory.StartNew(() => { foreach (KeyValuePair<string, string> entry in dicList) { ucProgress = new Progress(); ucProgress.Country = entry.Key; ucProgress.DBConnection = entry.Value; fpPanel.BeginInvoke((MethodInvoker)delegate { fpPanel.Controls.Add(ucProgress); ucProgress.Process(); }); //fpPanel.Controls.Add(ucProgress); System.Threading.Thread.SpinWait(5000000); } }); } private void Main_Resize(object sender, EventArgs e) { this.Invalidate(); } private void Main_Paint(object sender, PaintEventArgs e) { using (LinearGradientBrush brush = new LinearGradientBrush(this.ClientRectangle, Color.WhiteSmoke, Color.LightGray, 90F)) { e.Graphics.FillRectangle(brush, this.ClientRectangle); } } } 

user control code

 public partial class Progress : UserControl { public Progress() { InitializeComponent(); lblMsg.Text = ""; pbStatus.Minimum = 0; pbStatus.Maximum = 100; } public string Country { get; set; } public string DBConnection { get; set; } public string Sql { get; set; } public void SetMessage(string strMsg) { lblMsg.Text = strMsg; } public void Process() { var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext(); Task.Factory.StartNew(() => { lblMsg.BeginInvoke((MethodInvoker)delegate { lblMsg.Text = "Connecting country " + Country; }); pbStatus.BeginInvoke((MethodInvoker)delegate { pbStatus.Value = 30; }); System.Threading.Thread.SpinWait(50000000); //*********** lblMsg.BeginInvoke((MethodInvoker)delegate { lblMsg.Text = "executing sql for country " + Country; }); pbStatus.BeginInvoke((MethodInvoker)delegate { pbStatus.Value = 60; }); System.Threading.Thread.SpinWait(50000000); //*********** lblMsg.BeginInvoke((MethodInvoker)delegate { lblMsg.Text = "sql executed successfully for country " + Country; }); pbStatus.BeginInvoke((MethodInvoker)delegate { pbStatus.Value = 100; }); System.Threading.Thread.SpinWait(50000000); }); //System.Threading.Thread.SpinWait(50000000); // do work here } } 
+2
source share
1 answer

perhaps this could be the starting point. The pause / resume processing depends on your needs and is subject to change.

 var cancellationTokenSource = new CancellationTokenSource(); var cancellation = cancellationTokenSource.Token; void UpdateDatabases(IEnumerable<...> databases, CancellationToken cancellation) { foreach(db in databases) { //create as many ProgressBar instances as databases you want to update //check if ProgressBar exist, then return it and reuse, otherwise create new ProgressBar pb = new ProgressBar(); pb.Maximum = iterations; pb.Dock = DockStyle.Fill; Controls.Add(pb); //start thread for every database/progress bar Task.Factory.StartNew(progressBar => { var start = (ProgressBar)progressBar).Value; //use last value in case of pause Parallel.For(start, iterations, new ParallelOptions(){CancellationToken = cancellation} (i, loopState) => { if (loopState.ShouldExitCurrentIteration) return; //perhaps check loopState.ShouldExitCurrentIteration inside worker method Thread.SpinWait(50000000); // do work here BeginInvoke((Action)delegate { ((ProgressBar)progressBar).Value++; }); }); }, pb, cancellation) .ContinueWith(task => { //to handle exceptions use task.Exception member var progressBar = (ProgressBar)task.AsyncState; if (!task.IsCancelled) { //hide progress bar here and reset pb.Value = 0 } }, TaskScheduler.FromCurrentSynchronizationContext() //update UI from UI thread ); } } //......... //Call UpdateDatabases(databases, cancellation) //To suspend, call cancellationTokenSource.Cancel(); //To resume - simply call UpdateDatabases again cancellationTokenSource = new CancellationTokenSource(); cancellation = cancellationTokenSource.Token; UpdateDatabases(databases, cancellation) 

Update

I looked at your code. Take a look at the updated code and adapt it to your needs. The main mistakes are a mess with closures and making Progress from a thread. To enable parallel processing, you can use Parallel.ForEach (see MSND for possible overloads). Also the design looks a little strange to me (you mix data and logic in Progress ). From the point of view of the user interface, it is also strange that progress indicators are displayed in the processing order, but not in the original order, since they are in the list (this will be a problem if you decide to sort the list alphabetically)

Hope this helps.

main for code

  private void btnStart_Click(object sender, EventArgs e) { Progress ucProgress = null; Dictionary<string, string> dicList = new Dictionary<string, string>(); dicList.Add("GB", "conn1"); dicList.Add("US", "conn2"); dicList.Add("DE", "conn3"); fpPanel.Controls.Clear(); Func<KeyValuePair<string, string>, object> createProgress = entry => { var tmp = new Progress {Country = entry.Key, DBConnection = entry.Value}; fpPanel.Controls.Add(tmp); return tmp; }; Task.Factory.StartNew(() => { //foreach (KeyValuePair<string, string> entry in dicList) Parallel.ForEach(dicList, entry => { //create and add the Progress in UI thread var ucProgress = (Progress)fpPanel.Invoke(createProgress, entry); //execute ucProgress.Process(); in non-UI thread in parallel. //the .Process(); must update UI by using *Invoke ucProgress.Process(); System.Threading.Thread.SpinWait(5000000); }); }); } 

user management code

 public void Process() { //uiScheduler - Not used //var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext(); //The Task is not necessary because the Process() called from Parallel.ForEach //Task.Factory.StartNew(() => //{ //BeginInvoke or Invoke required lblMsg.BeginInvoke((MethodInvoker)delegate { lblMsg.Text = "Connecting country " + Country; }); pbStatus.BeginInvoke((MethodInvoker)delegate { pbStatus.Value = 30; }); System.Threading.Thread.SpinWait(50000000); //*********** lblMsg.BeginInvoke((MethodInvoker)delegate { lblMsg.Text = "executing sql for country " + Country; }); pbStatus.BeginInvoke((MethodInvoker)delegate { pbStatus.Value = 60; }); System.Threading.Thread.SpinWait(50000000); //*********** lblMsg.BeginInvoke((MethodInvoker)delegate { lblMsg.Text = "sql executed successfully for country " + Country; }); pbStatus.BeginInvoke((MethodInvoker)delegate { pbStatus.Value = 100; }); System.Threading.Thread.SpinWait(50000000); //}); //System.Threading.Thread.SpinWait(50000000); // do work here } 
+2
source

Source: https://habr.com/ru/post/1202154/


All Articles