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 >
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 >
上一篇: 人生最大的投资就是你自己。
推荐阅读
-
factory threads
-
从 LLaMA-Factory 项目中认识微调
-
llama-factory SFT 教程系列(3),chatglm3-6B 命名实体识别 - Lora 微调
-
从 LLaMA-Factory 项目中认识微调
-
Web 3D Factory - 智能数字可视化工厂 HTML5+WebGL(ThreeJS) 刻录杰作
-
Python - 深度学习系列 30 - 使用 LLaMA-Factory 微调模型 - 描述
-
来自 Byte Jump Factory 内部的超高质量 Flutter + Kotlin 笔记!技术能力真的很重要!移动架构师成长之路
-
(八)【Jmeter】线程(Threads(Users))之bzm - Concurrency Thread Group
-
(十三)【Jmeter】线程(Threads(Users))之tearDown 线程组
-
ddd战略解析:Factory与Specification的角色深度探讨