兔小贝儿歌,多线程是否一定能进步使用功能,看看下面测验就知道了,一生一世美人骨

咱们第一印象是多线程通常是比单线程有更加高的处理才能,假如单线程搞不定的作业经过多线程往往能够处理,但实际状况未必是这样。首要关于单CPU(中心)的状况,关于纯核算型的使命,多线程肯定是不如单线程处理的更快,这个咱们肯定能想清楚。但关于多核的状况,多线程的处理状况又是怎么呢?

检验程序设计与完成

线程上下文切换的功用损耗到底有多少,一向没有直观的了解,今日写个程序检验一下。先看看下面的程序(点击下载):

ThreadTester是一切Tester的基类。一切的Tester都干的是相同一件作业,把counter添加到100000000,每次只能加1。

 1: public abstract class ThreadTester
2: {
3: public const lon小萝莉小说g MAX_COUNTER_NUMBER = 100000000;
4:
5: private long _counter = 0;
6:
7: //取得计数
8: public virtual long GetCounter()
9: {
10: return this._counter;
11: }
12:
13: //添加计数器
14: protected virtual void Inc刘仪轩reaseCount饥饿小丑er()
15: {
16: this._counter += 1;
17: }
18:
19: //发动检验
20: public abstract void Start();
21:
22: //取得Counter从开端添加到现在的数字所耗的时刻
23: public abstract long GetElapsedMillisecondsOfIncreaseCounter();
24:
25: //检验是否正在运转
26: public abstract bool IsTesterRunning();
27: }

SingleThreadTester是单线程计数。

 1: class SingleThreadTester : ThreadTester
2: {
3: private Stopwatch _aStopWatch = new Stopwatch();
4:
5: public override void Start()
6: {
7: _aStopWatch.Start();
8:
9: Thread aThread = new Thread(() => WorkInThread());
10: aThread.Start();
11: }
12:
13: public override long GetElapsedMilli姬鹏飞之子姬赤军secondsOfIncreaseCounter()
14: {
15: return this._aStopWatch.ElapsedMilliseconds;
16: }
17:
18: public override bool IsTesterRunning()
19: {
20: return _aStopWatch.IsRunning;
21: }
22:
23: private void WorkInThread()
24: {
25: while (true)
26: {
27: if (this.GetCounter() > ThreadTester.MAX_COUNTER_NUMBER)
28: {
29: _aStopWatch.Stop();
30: break;
31: }
32:
33: this.IncreaseCounter();
34: }
35: }
36: }

TwoThreadSwitchTester是两个线程替换计数。

 1: class TwoThreadSwitchTester : ThreadTester
2: {
3: private Stopwatch _aStopWatch = new Stopwatch();
4: private AutoResetEvent _autoResetEve兔小贝儿歌,多线程是否必定能前进运用功用,看看下面检验就知道了,一生一世佳人骨nt = n梦境空中岛奇遇ew AutoResetEvent(false);
5:
6: public override void Start()
7: {
8: _aStopWatch.Start();
9:
10: Thread aThread1 = new Thread(() => Work1InThread());
11: aThread1.Start();
12:
13: Thread aThread2 = new Thread(() => Work2InThread());
14: aThread2.Start();
15: }
16:
17: public override long GetElapsedMillisecondsOfIncreaseCounter()
18: {
19: return this._aStopWatch.ElapsedMilliseconds;
20: }
21:
22: public override bool IsTesterRunning()
23: {
24: return _aStopWatc兔小贝儿歌,多线程是否必定能前进运用功用,看看下面检验就知道了,一生一世佳人骨h.IsRunning;
25: }
26:
27: private void Work1InThread()
28: {
29: while (true)
30: {
31: _autoResetEvent.WaitOne();
32:
33: this.IncreaseCounter();
34:
35: if (this.GetCounter() > ThreadT兔小贝儿歌,多线程是否必定能前进运用功用,看看下面检验就知道了,一生一世佳人骨ester.MAX_COUNTER_NUMBER)
36: {
37: _aStopWatch.Stop();
38: break;
39: }
40:
41: _autoResetEvent.Set();
42: }
43: }
44:
45: private void Work2InThread()
46: {
47: while (true)
48: {
49: _autoResetEvent.Set();
50: _autoReset兔小贝儿歌,多线程是否必定能前进运用功用,看看下面检验就知道了,一生一世佳人骨Event.WaitOne();
51: this.IncreaseCounter();
52:
53: if (this.GetCounter() > Thre哈尔贾adTester.MAX_COUNTER_NUMBER)
54: {
55: _aStopWatch.Stop();
56: break;
57: }
58: }
59: }
60: }

MultiThreadTester能够指定线程数,多个线程争抢计数。

 1: class MultiThreadTester : ThreadTester
2: {
3: private St兔小贝儿歌,多线程是否必定能前进运用功用,看看下面检验就知道了,一生一世佳人骨opwatch _aStopWatch = newrimming Stopwatch();
4: private readonly int _threadCount = 0;
5: private readonly object _counterLock = new object();
6:
7: public MultiThrea兔小贝儿歌,多线程是否必定能前进运用功用,看看下面检验就知道了,一生一世佳人骨dTester(int threadCount)
8: {
9: this._threadCount = threadCount;
10: }
11:
12: public override void Start()
13: {
14: _aStopWatch.Start();
15:
16: for (int i = 0; i < _threadCount; i++)
17: {
18: Thread aThread = new Thread(() => WorkInThread());
19: aThread.Start();
20: }
21: }
22:
23: public override long GetElapsedMillis双斑蟋蟀econdsOfIncreaseCounter()
24: {
25: return this._aStopWatch.ElapsedMilliseconds;
26: }
27:
28: public override bool IsTesterRunning()
29: {
30: return _aStopWatch.IsRunning;
31: }
32:
33: private voi王沁园d WorkInThread()
34: {
35: while (true)
36: {
37: lock (_counterLock)
38: {
39: if (this.GetCounter() > ThreadTester.MAX_COUNTER_NUMBER)
40: {
41: _aStopWatch.Stop();
42: 景坤科技break;
43: }
44:
45: this.IncreaseCounter();
46: }
47: }
48: }
49: }

Program的Main函数中,依据用户的挑选来决议履行哪个检验类。

 1: class Program
2: {
3: static void Main(朱兆德string[] args)
4: {
5:
6: string inputText = GetUserChoice();
7:
8: while (!"4".Equals(inputText))
9: {
10: ThreadTester tester = GreateThreadTesterByInputText(inputText);
11: tester.Start();
12:
13: while (true)
14: {
15: Console.WriteLine(GetStatusOfThreadTester(tester));
16: if (!tester.IsTesterRunning())
17: {
18: break;
19: }
20: Thread.Sleep(100);
21: }
22:
23: inputText = GetUserChoice();
24: }
25:
26: Console.Write("Click enter to exit...");
27: }
28:
29: private static string GetStatusOfThreadTester(ThreadTester tester)
30: {
31: return string.Format("[耗时{0}ms] counter = {1}, {2}",
32: tester.GetElapsedMillisecondsOfIncreaseCounter(), tester.GetCounter(),
33: tester.IsTesterRunning() ? "running" : "stopped");
34: }
35:
36: private static ThreadTester GreateThreadTesterByInputText(string inputText)
37: {
38: switch (inputText)
39: {
40: case "1":
41: return new SingleTh贵利王read兔小贝儿歌,多线程是否必定能前进运用功用,看看下面检验就知道了,一生一世佳人骨Tester();
42: case "2":
43: return new TwoThreadSwitchTester();
44: default:
45: return new MultiThreadTester(100);
46: }
47: }
48:
49: private static string GetUserChoice()
50: {
51: Console.WriteLine(@"==Please select the option in the following list:==
52: 1. SingleThreadTester
53: 2. TwoThreadSwitchTester
54: 3. MultiThreadTester
55: 4. Exit");
56:
57: string inputText = Console.ReadLine();
58:
59: return inputText;
60: }
61: }

三个检验类,运转成果如下:

Single Thread:

[耗时407ms] counter = 100000001, stopped

[耗时453ms] counter = 100000001, stopped

[耗时412ms] counter = 100000001, stopped

Two Thread Switch:

[耗时161503ms] counter = 100000001, stopped

[耗时164508ms] counter = 100000001, stopped

[耗时164201ms] counter = 100000001, stopped

Multi Threads - 100 Threads:

[耗时3659ms] counter = 100000001,朴延美 stopped

[耗时3950ms] counter = 100000001, 洪金州stopped

[耗时3720ms] counter = 100000001, stopped

Multi Threads - 2 Threads:

[耗时3078ms] counter = 100000001, stopped

[耗时3160ms] counter = 100000001, stopped

[耗时3106ms] counter = 100000001, stopped

什么是线程上下文切换

多使命体系往往需求一起履行多道作业。作业数往往大于机器的CPU数,但是一颗CPU一起只能履行一项使命,为了让用户感觉这些使命正在一起进行,操作体系的设计者奇妙有利地势用了时刻片轮转的方法,CPU给每个使命都效劳必定的时刻,然后把当前使命的状况保存下来,在加载下一使命的状况后,持续效劳下一使命。使命的状况保存及再加载,这段进程就叫做上下文切换。时刻片轮转的方法使多个使命在同一颗CPU上履行变成了或许,但一起也带来了保存现场和加载现场的直接耗费。(Note. 更精确地说, 上下文切换会带来直接和直接两种要素影响程序功用的耗费. 直接耗费包含: 政才老婆CPU寄存器需求保存和加载, 体系调度器的代码需求履行, TLB实例需求从头加载, CPU 的pipeline需求刷掉; 直接耗费指的是多核的cache之间得同享数据, 直接耗费关于程序的影响要看线程作业区操作数据的巨细).

检验成果剖析缚魂

依据上面上下文切换的界说,咱们做出下面的假定:

  1. 之所以TwoThreadSwitchTeste吴宗玲r履行速度最慢,因为线程上下文切换的次数最多,时刻首要耗费在上下文切换了,两个线程替换2004辣妹奸细之危机四伏计数,每计数一次就要做一次线程切换。
  2. “Multi Threads - 100 Threads”比“Mu烟凉忘情深lti Threads - 2 Threads”开的线程数量要多,导致线程切换次数也比后者多,履行时刻也比后者长。

因为Windows下没有像Linux下的vmstat这样的东西,这儿咱们运用Process Explorer看看程序履行的时分线程上线文切换的次数。

Single Thread:

计数期间,线程一共切换了580-548=32次。(548是发动程序后,初始的数值)

Two Thread Switch:

计数期间,线程一共切换了33673295-124=33673171次。(124是发动程序后,初始的数值)

Multi Threads - 100 Threads:

计数期间,线程一共切换了846-329=517次。(329是发动程序后,初始的数值)

Multi Threads - 2 Threads:

计数期间,线程一共切换了295-201=94次。(201是发动程序后,初始的数值)

从上面搜集的数据来看,和咱们的判别根本相符。

干活的其实是CPU,而不是线程

再想想本来学过的常识,之前一向认为线程多干活就快,简直是把学过的核算机原理都还给教师了。真实干活的不是线程,而是CPU。线程越多,干活不必定越快。

那么高并发的状况下什么时分合适单线程,什么时分合适多线程呢?

合适单线程的场景:单个线程的作业逻辑简略,并且速度非常快,比方从内存中读取某个值,或许从Hash表依据key取得某个value。Redis和Node.js这类程序都是单线程,合适单个线程简略快速的场景。

合适多线程的场景:单个线程的作业逻辑杂乱,等待时刻较长或许需求耗费很多体系运算资源,比方需求从多个长途效劳取得数据并核算,或许图画处理。

展开全文