欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

factory threads

最编程 2024-05-04 19:33:35
...
  1 .NET 2.0中真正的多线程实例
  2 Real Multi-threading in .NET 2.0
  3 remex1980 翻译于 2007-5-16 20:43:21 
  4 原作者: Jose Luis Latorre
  5 原文地址:http://www.codeproject.com/useritems/RealMultiThreading.asp
  6 
  7 
  8 多线程实例源代码(保留原文处链接)
  9 http://www.codeproject.com/useritems/RealMultiThreading/RealMultiThreading_src.zip
 10 
 11 
 12  
 13 
 14 
 15 简介
 16 
 17 多线程总是那么让人振奋。大家都希望能够同时处理很多事情,不过如果我们没有正确的硬件的话,我们很难达到这点。到目前为止,我们所做的只是分开CPU使用较多的工作,使其为后台进程,这样可以使得界面上不被阻塞。
 18 
 19 不过我希望能够得到更好的效果,并充分利用当前最新的多CPU效能。因此,我将写一个真正的多线程实例,将会有多个线程作为后台线程在运行。
 20 
 21 这就是这篇文章将要写的,不得不说的是,最终的结果实在是让我很激动。希望你也能够发觉它的用处。
 22 
 23 在有4个CPU的多CPU服务器上,我得到了280%的效果(测试的是CPU型的任务),在一些非CPU占用较多的任务中,它可以提高到500% 到1000%的性能。
 24 
 25 背景
 26 
 27 网上也有不少介绍.Net 2.0下的多线程的文章,应该说,我从它们中受益颇多。我正在使用的是BackgroundWorker .Net 2.0组件(不过也有实现在.net 1.1下的代码)。
 28 
 29 这里,我列出一些有用的文章链接:
 30 来自Paul Kimmel的很好的介绍性文章 http://www.informit.com/articles/article.asp?p=459619&seqNum=5&rl=1
 31 来自Juval Löwy的介绍性文章 http://www.devx.com/codemag/Article/20639/1954?pf=true
 32 (必看)Joseph Albahari的C#中使用线程 http://www.albahari.com/threading/part3.html
 33 Michael Weinhardt写的在Windows Forms 2.0中一个简单安全的多线程,我使用了这个网页中的CPU密集型任务,这是他从Chris Sell的文章中引用的。
 34 http://www.mikedub.net/mikeDubSamples/SafeReallySimpleMultithreadingInWindowsForms20/SafeReallySimpleMultithreadingInWindowsForms20.htm
 35 
 36 如果你对多线程世界仍然不是特别熟悉或者希望了解最新的.Net 2.0的 BackgroundWorker组件,那么应该好好读读上面的文章。
 37 
 38 
 39 
 40 提出的问题
 41 
 42 任何一个任务……无论是CPU密集型还是普通的任务:
 43 
 44 CPU密集型:它可以分成一个、两个或多个线程,每个线程会占用一个CPU(这样就使得程序的性能翻番)
 45 
 46 普通任务:每一个顺序执行的普通任务,在进行数据存储或使用一个web service的时候都会有一些延迟。所有的这些,都意味着这些没有使用的时间对于用户或任务本身来说有了浪费。这些时间将被重新安排,并将被并行的任务使用,不会再丢失。也就是说,如果有100个100ms延迟的任务,它们在单线程模型和20个线程模型的性能差距会达到1000%。
 47 
 48 
 49 我们说,如果要处理一个创建一个网站多个块的任务,不是顺序的执行,而是花1-4秒钟把所有的section创建好;商标,在线用户,最新文章,投票工具等等…… 如果我们能够异步地创建它们,然后在发送给用户,会怎么样?我们就会节省很多webservice的调用,数据库的调用,许多宝贵的时间……这些调用会更快地执行。看上去是不是很诱人?
 50 
 51 
 52 
 53 解决方案如下:
 54 
 55 调用BackgroundWorker,正如我们想要的那样,我们会继承它。后台worker会帮助我们建立一个“Worker”,用于异步地做一个工作。
 56 
 57 我们想做的是建立一个工厂Factory(只是为了面向对象的设计,于设计模式无关),任务会放在在这个Factory中执行。这意味着,我们将有一类的任务,一些进程,一些知道如何执行任务的worker。
 58 
 59 当然我们需要一个负责分配任务给这些worker的manager,告诉这些worker当它们做完一步或全部时,做什么事情。当然,我们也需要manager能够告诉worker停止当前的任务。它们也需要休息啊:)当manager说停止的时候,它们就应该停止。
 60 
 61 我们将会从底至上地解释这些,首先从Worker说起,然后再继续Manager。
 62 
 63 Worker
 64 
 65 
 66 它是Background worker的继承类,我们构建一个构造函数,并分配两个BackgroundWorker的属性,分别是WorkerReportsProgress 和WorkerSupportsCancellation,它们的功能就向其名字的意义一样:报告进度,停止任务。每个Worker还有一个id,Manager将会通过这个id控制它们。
 67 
 68 
 69 public class MTWorker : BackgroundWorker
 70    {
 71       #region Private members
 72       private int _idxLWorker = 0;
 73       #endregion
 74 
 75       #region Properties
 76       public int IdxLWorker
 77       {
 78          get { return _idxLWorker; }
 79          set { _idxLWorker = value; }
 80       }
 81       #endregion
 82 
 83       #region Constructor
 84       public MTWorker()
 85       {
 86          WorkerReportsProgress = true;
 87          WorkerSupportsCancellation = true;
 88       }
 89       public MTWorker(int idxWorker)
 90          : this()
 91       {
 92          _idxLWorker = idxWorker;
 93       }
 94       #endregion
 95 
 96 
 97 另外,我们将重载BackgroundWorker的一些函数。事实上,最有意思的是,究竟谁在做真正的工作?它就是OnDoWork,当我们invoke或者启动多线程的时候,它就会被调用。在这里,我们启动任务、执行任务、取消和完成这个任务。
 98 我加了两个可能的任务,一个是普通型的,它会申请并等待文件系统、网络、数据库或Webservices的调用。另一个是CPU密集型的任务:计算PI值。你可以试试增加或减少线程数量后,增加或是减少的延迟(我的意思是增减Worker的数量)
 99 
100 
101 
102 OnDoWork方法的代码
103 
104 
105 protected override void OnDoWork(DoWorkEventArgs e)
106 {
107    //Here we receive the necessary data for doing the work 
108    //we get an int but it could be a struct, class, whatever..
109    int digits = (int)e.Argument;
110    double tmpProgress = 0;
111    int Progress = 0;
112    String pi = "3";
113 
114    // This method will run on a thread other than the UI thread.
115    // Be sure not to manipulate any Windows Forms controls created
116    // on the UI thread from this method.
117    this.ReportProgress(0, pi);
118    //Here we tell the manager that we start the job..
119 
120    Boolean bJobFinished = false;
121    int percentCompleteCalc = 0;
122    String TypeOfProcess = "NORMAL"//Change to "PI" for a cpu intensive task
123    //Initialize calculations
124    while (!bJobFinished)
125    {
126       if (TypeOfProcess == "NORMAL")
127       {
128          #region Normal Process simulation, putting a time 
129                  delay to emulate a wait-for-something
130          while (!bJobFinished)
131          {
132             if (CancellationPending)
133             {
134                e.Cancel = true;
135                return//break
136             }
137             //Perform another calculation step
138             Thread.Sleep(250);
139             percentCompleteCalc = percentCompleteCalc + 10;
140             if (percentCompleteCalc >= 100)
141                bJobFinished = true;
142             else
143                ReportProgress(percentCompleteCalc, pi);
144          }
145          #endregion
146       }
147       else
148       {
149          #region  Pi Calculation - CPU intensive job, 
150                   beware of it if not using threading ;) !!
151          //PI Calculation
152          if (digits > 0)
153          {
154             pi += ".";
155             for (int i = 0; i < digits; i += 9)
156             {
157                // Work out pi. Scientific bit :-)
158                int nineDigits = NineDigitsOfPi.StartingAt(i + 1);
159                int digitCount = System.Math.Min(digits - i, 9);
160                string ds = System.String.Format("{0:D9}", nineDigits);
161                pi += ds.Substring(0, digitCount);
162 
163                // Show progress
164                tmpProgress = (i + digitCount);
165                tmpProgress = (tmpProgress / digits);
166                tmpProgress = tmpProgress * 100;
167                Progress = Convert.ToInt32(tmpProgress);
168                ReportProgress(Progress, pi);
169                // Deal with possible cancellation
170                if (CancellationPending) //If the manager says to stop, do so..
171                {
172                   bJobFinished = true;
173                   e.Cancel = true;
174                   return;
175                }
176             }
177          }
178          bJobFinished = true;
179          #endregion
180       }
181 
182    }
183    ReportProgress(100, pi); //Last job report to the manager ;)
184    e.Result = pi; //Here we pass the final result of the Job
185 }
186 Manager
187 
188 这是一个很有趣的地方,我确信它有很大的改进空间-欢迎任何的评论和改进!它所做的是给每个线程生成和配置一个Worker,然后给这些Worker安排任务。目前,传给Worker的参数是数字,但是它能够传送一个包含任务定义的类或结构。一个可能的改进是选择如何做这些内部工作的策略模式。
189 
190 调用InitManager方法配置任务,和它的数量等属性。然后,创建一个多线程Worker的数组,配置它们。
191 配置的代码如下:
192 
193 
194 private void ConfigureWorker(MTWorker MTW)
195 {
196   //We associate the events of the worker
197   MTW.ProgressChanged += MTWorker_ProgressChanged; 
198   MTW.RunWorkerCompleted += MTWorker_RunWorkerCompleted; 
199 }
200 
201 Like this, the Worker’s subclassed thread management Methods are linked to the Methods held by the Manager. Note that with a Strategy pattern implemented we could assign these to the proper manager for these methods.
202 最主要的方法是AssignWorkers,它会检查所有的Worker,如果发现没有任务的Worker,就分配一个任务给它。直到扫描一遍之后,没有发现任何有任务的Worker,这样就意味这任务结束了。不需要再做别的了!
203 
204 代码如下:
205 
206 public void AssignWorkers()
207 {
208    Boolean ThereAreWorkersWorking = false;
209    //We check all workers that are not doing a job and assign a new one
210    foreach (MTWorker W in _arrLWorker)
211    {
212       if (W.IsBusy == false)
213       {
214          //If there are still jobs to be done 
215          //we assign the job to the free worker
216          if (_iNumJobs >

上一篇: 人生最大的投资就是你自己。

下一篇: 职业学校云教室建设部署腾创NComputing软硬件一体化终端解决方案