想要使用带有进度报告的任务并行库来更新数据库 [英] Want to use Task Parallel Library with progress reporting for updating database

查看:48
本文介绍了想要使用带有进度报告的任务并行库来更新数据库的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我开发了一个存储多个连接字符串的应用程序.我只是在 for 循环中迭代并连接每个 db 并针对每个 db 执行 sql.通过这种方式,我使用批量 sql 语句更新多个数据库.

i developed a application where multiple connection string are stored. i just iterate in for loop and connect each db and execute sql against each db. in this way i update multiple database with bulk sql statement.

现在我需要使用现在的任务并行库来同时更新多个数据库,而不是一个一个地循环更新.我还想处理异常,还想为多个数据库操作显示多个进度条.暂停&恢复功能应该在那里.

Now i need to use now Task Parallel Library to update multiple db simultaneously instead of updating one after one in loop. i also want to handle exception and also want to show multiple progress bar for multiple database operation. pause & resume functionality should be there.

当我单击按钮时,将打开多个数据库连接,并且对于每个任务,我的表单中都会添加一个新的进度条.每个进度条都会显示每个数据库操作的进度.当任何任务完成时,相应的进度条将从表单中删除.

when i will click on button then multiple db connection will be open and for each task a new progress bar will be added in my form. each progress bar will show each db operation progress. when any task will be finish then that corresponding progress bar will be removed from form.

任何人都可以通过示例代码指导我如何使用 TPL 进行操作.在这里,我得到了一个更新一个进度条的代码,但我需要更新多个进度条.整数迭代 = 100;

any one can guide me with sample code how to do it using TPL. here i got one code which update one progressbar but i need to update multiple progress bar. 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++; });   
    });   
}); 

更新问题

我就是这样做的.代码有效,但所有进度条值依次增加.我在 winform 应用程序中有一个表单和一个用户控件.请查看我的代码并告诉我那里有什么问题.

UPDATE Question

i did it in this way. code works but all progress bar value increase sequentially. i have one form and one user control in winform apps. please have a look at my code and tell me what is wrong there.

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

用户控制代码

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

推荐答案

也许可以作为起点.处理暂停/恢复取决于您的需要,可以进行调整.

perhaps it can be starting point. Handling pause/resume depends from your needs and can be tweaked.

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)  

更新

我已经查看了您的代码.查看重新访问的代码并根据您的需要进行调整.主要错误 - 使用 closures 并从非 ui 线程创建 Progress.要启用并行处理,您可以使用 Parallel.ForEach(有关可能的重载,请参阅 MSND).而且这个设计对我来说看起来有点奇怪(你在 Progress 中混合了数据和逻辑).从 UI 的角度来看,同样奇怪的是,进度条会按处理顺序出现,而不是按照它们在列表中的原始顺序(如果您决定按字母顺序对列表进行排序,这将是一个问题)

I've reviewed your code. Take a look at the revisited code and adapt it to your needs. Main mistakes - mess with closures and creating the Progress from non-ui thread. To enable parallel processing you can use Parallel.ForEach (see MSND for possible overloads). Also the design looks little bit strange for me(you're mixing data and logic in the Progress ). From UI perspective it's also strange that progress bars will appear in order of processing but not in original order as they are in list (it will be a problem if you decide to sort the list alphabetically)

希望能帮到你.

主要代码

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

    }

用户控制代码

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   
    }

这篇关于想要使用带有进度报告的任务并行库来更新数据库的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆