ERP/MIS 开发 多线程模式与应用(开放源码)

一直从事ERP/MIS开发,总结一下,自己在ERP/MIS开发中,经常用到的应用多线程的两种模式。

 

先举一个例子,以帮忙回忆起对多线程的印象。CopyFilesProc是实现拷贝文件的一个方法,用多线程调用:

  Thread simpleThread = new Thread(CopyFilesProc);

  simpleThread.Name = "CopyFiles";

  simpleThread.Start();

启动调用,在VS2010中,增加了线程调试窗口,以查看当前进程的线程。我的理解是,总是记得给你的线程命名。

image

 

1 需要与界面互动的场景,需要实现报告进度,应用BackgroundWorker组件

image

如图,月结的界面效果图,当用户点击Process按钮后,出现进度条,显示处理进度
对于BackgroundWorker控件的运用,请查阅MSDN知识库。我这里对报告进度这一小功能,作说明。

报告进度的功能,分两种情况来实现。一种是处理任务(Job)没有用接口实现,当前进度变量存在于窗体中,源码如下

private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
       {
           bgWorker.ReportProgress(10, fileName);            
        }

直接在DoWork事件中,调用ReportProgress方法,如果可能,还可以传入变量值

private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
       {
           string fileName = e.UserState.ToString();
        }

注册事件ProgressChanged来输出当前正在处理的项目,在这个方法中可以操作UI控件。

 

另一种方法是,处理任务的实现(Job的功能实现)被隔离到第三方的类库中,说白了就是放到外部的类文件中,不与窗体代码混淆在一起。代码举例,对于月结功能,可能的一个这样的实现

public interface IJob
    {
        void Execute();
     }

实现文件

public class Job : IJob
   {

     public void Execute()
       { 

       }

    }

在窗体的bgWorker_DoWork方法中,调用实现job.Execute()来这现月结功能。

这种方法下,报告进度的功能稍微复杂一点,要用到定时器,进度存放变量值,执行任务时更新进度。
实现步骤如下,在接口实现文件,定义存放进度变量的集合值_JobProcess变量,

public class Job : IJob
   {
       private static ConcurrentDictionary<int, bool> _JobProcess;

在执行长时间任务前,初始化进度变量

public void Execute()
        {
            if (_JobProcess == null || _JobProcess.Count== 0||_JobProcess.Count>0)
            {
                _JobProcess = new ConcurrentDictionary<int, bool>();
                for (int i = 1; i <= _Step; i++)
                {
                    _JobProcess.TryAdd(i, false);
                }
            }

_Step是步长,这里有简化处理。实际操作中,要对任务进行分类处理,以达到分步的目的。比如,核算客户往来帐目,就以客户为依据来分步长,有121个客户,这里_Step就是121,然后每处理完一个客户的帐目,就把它设置为已经处理,即这样来调用

for (int i = 1; i <= _Step; i++)
           {
               _JobProcess[i] = true;
               Thread.Sleep(2000);
           }

每处理一个任务,就把它的时度标志为true,以表示已经处理。这样,在进度反馈接口中,可以及时反馈结果

public object[] GetExecuteStatus()
        {

              //已经处理完成

              int completedCount = (from item in _JobProcess
                                                   where item.Value == true
                                                   select item).Count();

               //当前总任务数量
                int totalCount = (from item in _JobProcess                                
                                  select item).Count();

                //正在处理的任务

                int itemInProcess = (from item in _JobProcess
                                        where item.Value == false
                                        orderby item.Key
                                        select item.Key).FirstOrDefault();

                 }

将这三个值返回到界面的timer的Tick事件中,实时改变界面的ProgressBar的状态,以达到报告进度的目的。

 

当完成功能需要一定的时间,一般定为超过20second,都应该用BackgroundWorker来处理,以保持界面及时响应。

 

2 不需要界面发生互动,后台运行完成后,显示结果即可,应用WorkerThreadBase模式

这个模式最主要的实现类是WorkerThreadBase,

public abstract class WorkerThreadBase : IDisposable
    {
        private Thread _workerThread;
        private ManualResetEvent _stopping;
        private ManualResetEvent _stopped;
        private bool _disposed;
        private bool _disposing;

使用了ManualResetEvent来同步多个线程,MSDN中对ManualResetEvent的解释是:通知一个或多个正在等待的线程已发生事件. 如果不能明白它的意思,就跑一下测试代码,来看看用途

DummyWorker dummyWorker = new DummyWorker();
dummyWorker.Start();

CopyFileWorker copyFileWorker = new CopyFileWorker(_copyInfo);
copyFileWorker.Start();

//wait for the two threads to finish
WorkerThreadBase.WaitAll(copyFileWorker, dummyWorker);

如代码所示,创建2个工作线程,工作线程的创建方法如下

public class DummyWorker:WorkerThreadBase
    {
        protected override void Work()
        {

         }

}

继承于WorkerThreadBase类型,重写Work方法即可。

当调用WorkerThreadBase.WaitAll停止当前线程,等待所有的线程运行完毕后,然后可以显示结果。


总结,后一种模式,编程简单,效率也高一些;在WinForm应用中,也常常在Forms类型的方法中,动态创建BackgroundWorker然后调用它,以保持UI继续接受用户输入。

 

请到epn.codeplex.com中下载源代码。

posted @ 2011-08-16 09:16  信息化建设  阅读(3069)  评论(6编辑  收藏  举报