如何更好使用多线程

        说到线程相信很多开发人员都会认为只要使用了多线程技术服务性能就会提高很多,但涉及过渡使用问题就很少人去了解。在使用上更多是了解是创建,使用,销毁或使用线程池之类的。但这些资料更多是如何使用线程,但对于应用怎样针对性规划线程让应用发挥出更好的性能则很难有更详细的资料来讲述。

        在硬件层面相信大家应该知道一个CPU核心只能运行一个线程,对于一个8逻辑核心的CPU同时工作线程是8个。但在软件服务应用中很多时候进程都开启远超过CPU核心数工作线程,这主要原因是在应用中往往涉及到IO这样低效率工作,为了确保CPU可以更好地继续其他的工作,系统会把等待IO完成线程加入到调度环节等待,然后由其它需要的任务线程补上。所以为了满足服务应用需要系统的逻辑线程远高于工作线程数。

线程工作饱和度

        线程工作饱和度相信很少人去了解它,只知道多线程可以更多地使用CPU资源,达到一个更高效的处理。在进程中线程有创建时间和CPU工作时间,不同的代码对线程使用的CPU时间也有所不同; 在服务应用中最常见的代码可以划分两种:一种是高速的内存运算,别一种则是IO操作(数据库,文件或其他网络服务等)。两种代码引起线程的工作饱和度都有着很大的差别,接下来通过简单的代码来看一下不同代码引起的线程工作量问题。

static void Memory()
{while (true){System.Threading.Interlocked.Increment(ref mCount);}
}

以上是一个简单内存累加方法,由于没有IO操作所以类似于CPU自悬状态,这一操作会一个占用比较多的CPU资源 

static void IO()
{while (true){Console.WriteLine(mCount);System.Threading.Interlocked.Increment(ref mCount);}
}

同样一个代码增加了一个控制台输出,由于Console存在输出IO操作这类操作比内存操作要慢,所以该操作会占用比较少的CPU

接下来两个代码使用4和8个线程运行情况又怎样呢?

Memory(4线程)

Memory(8线程)

IO(4线程)

IO(8线程)

测试可以发现纯内存操作随着线程的增加CPU使用率也成正比增加,而涉及到IO的操作的测试中4线程已经达到的IO操作的饱和量了,在往上加线程也无法达到更高的操作。所以当在创建多线程的时候,要确保创建的线程是否过量。就拿Memory函数来说,当CPU是8逻辑核的情况,再往上压到10线程已经没有提交效率的意义了,反而增加了线程调度的损耗。同样在IO操作也是,受IO限制开启再多的线程也无法提高IO操作效率(所以这种操作一般异步配合线程池回调来更好利用线程资源)。

规划准则

        从上面的测试可以反映出普遍使用线程场景的需求,当存在大量内存运算的时候总线程数尽量不要超过CPU的逻辑核数;而针对IO场景的应用规划线程数据超过IO负载能力。CPU有自己的处理想极限,不同IO也同样有处理极限;当到达相关资源极限的时候创建再多的线程意义不大,不但不能增加处理的效率,反而会引起线程过多影响性能的情况出现。在规划中要以线程最大饱和度作为规划的依据,最少线程资源最大化的目标让使用到的线程都得到最大化运行饱和度。

高效使用多线程

        提高性能不应该以线程多少来进行规划使用,首要目标是提升线程的工作饱和度。提高线程工作饱和度最主要方式就是线程复用,而线程池正是达到这种方式的最佳途径。但系统任务多样性,一个全局的线程池是无法达到更的分配,所以想在多线程上更用得更灵活,需要针对不同的业务场和资源来制定任务队列来控制线程的开销。

        以下是aspcore针对socket io封装的任务处理队列,在execute方法实现可以看到尽可以让线程处于工作状态,不要轻易回到收到全局的线程池中。

namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.Internal
{internal class IOQueue : PipeScheduler, IThreadPoolWorkItem{private readonly ConcurrentQueue<Work> _workItems = new ConcurrentQueue<Work>();private int _doingWork;public override void Schedule(Action<object?> action, object? state){_workItems.Enqueue(new Work(action, state));// Set working if it wasn't (via atomic Interlocked).if (Interlocked.CompareExchange(ref _doingWork, 1, 0) == 0){// Wasn't working, schedule.System.Threading.ThreadPool.UnsafeQueueUserWorkItem(this, preferLocal: false);}}void IThreadPoolWorkItem.Execute(){while (true){while (_workItems.TryDequeue(out Work item)){item.Callback(item.State);}// All work done.// Set _doingWork (0 == false) prior to checking IsEmpty to catch any missed work in interim.// This doesn't need to be volatile due to the following barrier (i.e. it is volatile)._doingWork = 0;// Ensure _doingWork is written before IsEmpty is read.// As they are two different memory locations, we insert a barrier to guarantee ordering.Thread.MemoryBarrier();// Check if there is work to doif (_workItems.IsEmpty){// Nothing to do, exit.break;}// Is work, can we set it as active again (via atomic Interlocked), prior to scheduling?if (Interlocked.Exchange(ref _doingWork, 1) == 1){// Execute has been rescheduled already, exit.break;}// Is work, wasn't already scheduled so continue loop.}}private readonly struct Work{public readonly Action<object?> Callback;public readonly object? State;public Work(Action<object?> callback, object? state){Callback = callback;State = state;}}}
}

以上是aspcore Kestrel模块的网络接收任务队列实现,在techempower的plaintext测试中配置了8个任务队列来完成相关操作,性能达到这项测试的最高。测试硬件资源是40线程,而aspcore Kestrel使用了8个线程来处理这一块达到最高的性能状态。

        同样BeetleX也实现类似的任务队列,只是为了使用方便通过队列组的方式进行定义和处理。

    public class DispatchCenter<T> : IDisposable{List<SingleThreadDispatcher<T>> mDispatchers = new List<SingleThreadDispatcher<T>>();long mIndex = 1;public DispatchCenter(Action<T> process) : this(process, Math.Min(Environment.ProcessorCount, 16)){}public DispatchCenter(Action<T> process, int count){for (int i = 0; i < count; i++){mDispatchers.Add(new SingleThreadDispatcher<T>(process));}}public void SetErrorHaneler(Action<T, Exception> handler){if (handler != null){foreach (var item in mDispatchers){item.ProcessError = handler;}}}public void Enqueue(T data, int waitLength = 5){if (waitLength < 2){Next().Enqueue(data);}else{for (int i = 0; i < mDispatchers.Count; i++){var item = mDispatchers[i];if (item.Count < waitLength){item.Enqueue(data);return;}}Next().Enqueue(data);}}public int Count{get{int count = 0;foreach (var item in mDispatchers)count += item.Count;return count;}}public SingleThreadDispatcher<T> Get(object data){int id = Math.Abs(data.GetHashCode());return mDispatchers[id % mDispatchers.Count];}public SingleThreadDispatcher<T> Next(){return mDispatchers[(int)(System.Threading.Interlocked.Increment(ref mIndex) % mDispatchers.Count)];}public void Dispose(){foreach (SingleThreadDispatcher<T> item in mDispatchers){item.Dispose();}mDispatchers.Clear();}}

总结

        其实针对多线程的使用并不能简单地创建线程即可,实际应用中需要考虑代码占用的CPU资源情况和具体CPU资源做一个规则调整才能更好的发挥出更高效的作用。所以在设计应用时一般都依据不同业务定义不同的任务队列,并定义相关配置参数,确保服务应用在不同硬件环境配置出更优化的处理性能状态。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/302858.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java 接口的静态方法_Java8新特性:接口的默认方法与接口的静态方法

默认方法允许接口方法定义默认实现&#xff0c;子类方法不必须实现此方法而就可以拥有该方法及实现。如下&#xff1a;public interface DefaultFuncInter {int getInt();default String getString(){return "Default String";}}默认方法的优势默认方法主要优势是提供…

前端又一本升级版图书上市了,听说比第一版还好看

哇&#xff01;听说《Node.js实战&#xff08;第2版&#xff09;》来了&#xff1f;没错&#xff01;这本让读者久等了的书&#xff0c;终于上市啦&#xff01;最近的升级版图书还是很多的&#xff0c;但是小伙伴对这本的期待值依旧不减&#xff01;毕竟第一版在豆瓣上获得了 8…

人气TOP|当红炸子鸡「小明机器人」,出道走花路啦

在全球新一轮技术革命的时代背景下&#xff0c;越来越多的企业走上了数字化之路。伴随着企业对数字化转型的持续关注&#xff0c;各行各业对“数字化员工”即RPA&#xff08;机器人流程自动化&#xff0c;Robotic Process Automation&#xff09;的需求也越发旺盛&#xff0c;都…

MFC和Win32之三___CGdiObject类和windows Gdi对象

小结&#xff1a; 前面讲到的windows窗口对象&#xff0c;在windows下用句柄来代表之&#xff0c;并且用了一个数据结构WNDCLASS&#xff08;窗口类&#xff09;来描述之。同理&#xff0c;windows的Gdi对象也有一些句柄来代表之&#xff08;比如hPen等&#xff09;&#xff0c…

java io流 教程_Java基础教程:IO流与文件基础

Java:IO流与文件基础说明&#xff1a;本章内容将会持续更新&#xff0c;大家可以关注一下并给我提供建议&#xff0c;谢谢啦。走进流什么是流流&#xff1a;指的是从源到目的地的字节的有序序列。在Java中&#xff0c;可以从其中读取一个字节序列的对象称作 输入流&#xff0c;…

用数学模型向你解释离婚

A Mathematical Model of Sentimental Dynamics Accounting for Marital Dissolution解释离婚的情感动力学数学模型背景西方社会的离婚是普遍存在的。它提出了重大的科学和社会学问题&#xff0c;不管是理论上还是解决方式上。学者和问题处理专家认为存在一种情感关系热力学第二…

记一次 .NET医疗布草API程序 内存暴涨分析

一&#xff1a;背景 1. 讲故事我在年前写过一篇关于CPU爆高的分析文章 再记一次 应用服务器 CPU 暴高事故分析 &#xff0c;当时是给同济做项目升级&#xff0c;看过那篇文章的朋友应该知道&#xff0c;最后的结论是运维人员错误的将 IIS 应用程序池设成 32bit 导致了事故的发生…

自已做的第一个autoconf程序(不断完善中)

2019独角兽企业重金招聘Python工程师标准>>> 1、先写个简单的main函数&#xff0c;并按如下目录结构存放&#xff1a; timesync-- |--conf |--include |--lib |--src 2、在最上层目录下&#xff0c;执行autoscan&#xff0c;生成configure.scan&#xff0c;并改名为…

3des加密 java php_php 3DES加密如何兼容Java

Java源码&#xff1a;import java.security.Security;import javax.crypto.Cipher;import javax.crypto.SecretKey;import javax.crypto.spec.SecretKeySpec;public class ThreeDES {private static final String Algorithm "DESede"; //定义 加密算法,可用 DES,DES…

轻松看懂机器学习十大常用算法

通过本篇文章大家可以对ML的常用算法形成常识性的认识。没有代码&#xff0c;没有复杂的理论推导&#xff0c;仅是图解&#xff0c;介绍这些算法是什么以及如何应用&#xff08;例子主要是分类问题&#xff09;。以后有机会再对单个算法做深入地解析。今天的算法如下&#xff1…

撸码是需要直觉的

随着撸码的时间增加&#xff0c;码感也会加强&#xff0c;今天看一个编码直觉案例吧&#xff01;案例&#xff1a;需求&#xff1a;把查询到的数据列表生成csv文件nuget包&#xff1a;CsvHelper数据实体类&#xff1a;/// <summary>/// 商品/// </summary>public c…

linux视频教程之vsftp_B

这次主要说一下VSFTP的扩展应用 max_clients100  最大连接数max_per_ip5   每个IP最大连接数local_max_rate500000 本地用户传输的最大数anon_max_rate200000 匿名用户传输的最大数单位是字节这个就要等一会了&#xff0c;我刚才不是只设了50嘛如果是没设的话…

java collator_Java Collator compare(String, String)用法及代码示例

java.text.Collat​​or类的compare()方法用于比较两个字符串的强度&#xff0c;并根据结果返回0&#xff0c;正值和负值作为输出。用法:public abstract int compare(String source,String target)参数&#xff1a;此方法需要两个字符串之间进行比较。返回值&#xff1a;如果第…

MATLAB常用算法与应用实例分享来袭!

小天从大学开始接触数学建模&#xff0c;便开启资料收集功能。经过近几年的积累和沉淀&#xff0c;再加上对数学建模领域的深入研究&#xff0c;收集整理了丰富的数学建模资料&#xff0c;内容涵盖“MATLAB常用算法”&#xff0c;“MATLAB算法应用实例”等。截止到今天&#xf…

git的安装与使用(一)--windows平台 .

1、Create github Account &#xff08;在github网站上创建一个账号&#xff09; eg. you used the email: taitoyahoo.com 2、Download && install MsysGit (下载windows平台下的git软件) Then open the git bash , input the commends $ git config --global…

C# WPF项目实战(经典)

目的&#xff1a;输出两台摄像头图像和两路设备图像&#xff0c;每一路设备截图6张主要知识&#xff1a;1. 通过SDK调取摄像头图像&#xff0c;并对图像进行剪裁&#xff1b;2. WPF中定时器DispatcherTimer用法&#xff1b;3. WPF中跨线程访问控件方法Dispatcher.Invoke((Actio…

4个终于被破译的世界级密码

全世界有3.14 % 的人已经关注了数据与算法之美很多时候&#xff0c;一个设计精巧的密码就像数学难题一样&#xff0c;许许多多难以破解的密码让人青丝泛白&#xff0c;至今仍未见天日。不过&#xff0c;也有一些密码中的幸运儿&#xff0c;最终仍然迎来了真相大白的那天。秘密组…

NET问答: 如何在 ASP.NET Core 的 .json 文件中读取 AppSettings ?

咨询区 Oluwafemi&#xff1a;在 appsettings.json 中我有如下的 AppSettings 实体数据&#xff0c;如下代码所示&#xff1a;{"AppSettings": {"token": "1234"} }我在网上搜了很久&#xff0c;寻找如何从 .json 文件中获取 AppSettings 实体&a…

java虚拟机工作原理图_Java虚拟机工作原理

首先我想从宏观上介绍一下Java虚拟机的工作原理。从最初的我们编写的Java源文件(.java文件)是如何一步步执行的&#xff0c;如下图所示&#xff0c;首先Java源文件经过前端编译器(javac或ECJ)将.java文件编译为Java字节码文件&#xff0c;然后JRE加载Java字节码文件&#xff0c…

如何快速测试与数据库的连接并得到连接字符串

刚做程序开发的人&#xff0c;常常为如何连接数据库&#xff0c;怎么写连接字符串而困惑。做产品安装的服务人员&#xff0c;也常常为如何快速测试本机与数据库的连接状况而头疼。这里&#xff0c;给出一个简单快速的实现方法&#xff1a;*.udl文件。 第一步&#xff1a;创建“…