本文是.Net中的并行编程第六篇,今天就介绍一些我在实际项目中的一些常用优化策略

     一、避免线程之间共享数据

避免线程之间共享数据主要是因为锁的问题,无论什么粒度的锁,最好的线程之间同步方式就是不加锁,这个地方主要措施就是找出数据之间的哪个地方需要共享数据和不需要共享数据的地方,再设计上避免多线程之间共享数据。

在以前做过的某项目,开始时设计的方案:

开始设计时所有的数据都放入到了公共队列,然后队列通知多个线程去处理数据,队列采用互斥锁保证线程同步,造成的结果就是线程进行频繁的上下切换,CPU时间都浪费在了上下文切换上从而导致队列拥堵无法及时处理数据,最终程序因无法处理数据而内存溢出。

一开始解决这个问题是想到了采用更细粒度的锁如原子操作,但使用原子操作又需要回退机制保证活锁问题还要防止浪费CPU的时间等等一系列的问题。。,如果开发完成其实就相当于自己实现了一个如lock之类的互斥锁。所以重新整理了业务需求,改良后的设计如下:

在数据输入进来以后采用一个单独的数据转发器分发到不同的队列,这样就避免了线程之间的竞争,数据转发器其实就相当于我们开发网站时用到了负载均衡器,而且可以根据队列里面的数据不同选择不用的队列或者可以结合CPU,内存使用率进行动态调度。当然改良后的设计满足了性能需求。

所以在进行多线程开发时,尽量避免使用锁,对于必须使用锁的情景要选择合适的锁,对于选择什么类型的锁,我的原则是:能满足性能要求就好,不要刻意追求细粒度的锁。粗粒度的锁性能低但易于使用和理解,细粒度的锁性能高但难以使用和理解,关于操作系统的锁的介绍可参考《windows核心编程》线程同步的章节,在.net平台下也可参考《CLR VIA C#》线程相关章节介绍。

二、注意CPU高速缓存失效,避免频繁的上下文切换。

       开发多核程序时高速缓存往往是提高性能的关键,这里的缓存指的是CPU的缓存(L1,L2,L3)。缓存运用得当往往能提高2倍以上的性能。

1.造成CPU缓存失效的原因有:

(1)频繁的修改内存中的数据

(2)使用了原子操作,lock锁等同步方式。

(3)线程上下文的切换。

(4)伪共享造成频繁的刷新高速缓存。

2. 关于频繁的上下文切换造成的原因有:

(1)程序本身的线程都在抢夺CPU资源,也就是CPU无法调度其他的线程,

(2)很多线程都在等待获取互斥锁但是只有一个线程能获取,其他线程不断在唤醒和睡眠之间切换。

3.以上问题的解决方案(只供参考,项目不同方案不同):

(1)避免使用任何形式的锁。

(2)在满足性能的前提下,用最少的线程做最少的事。

(3)再设计上避免修改数据,如以前开发的实时计算的程序,所有的数据只允许读取不允许修改,需要修改则创建一条新数据来代替,类似于Erlang程序的开发方式。

具体有关高速缓存基础内容可以参考《深入理解计算机操作系统》第六章的内容。

三、线程池的的使用场景和注意点:

       (1)对于执行时间较短的任务都应当交给线程池去处理而不是开启一个新线程,对于需要长时间处理的任务交给单独的线程去处理。

(2)对于读写文件的任务不要交给线程池处理,因为线程池内的线程属于后台线程,应用程序意外关闭后线程也关闭从而丢失数据。

(3)永远不要自己开发线程池,真正能用到产品级别线程池需要几个月的时间而且需要大量的测试,否则遇到问题悔之晚矣。

四、关于NUMA架构机器的优化。

        (1)现代服务器基本都是NUMA架构,在这些机器上我们编写程序时最好开启.net的服务端垃圾回收模式,这样我们分配对象时就能在最近使用CPU对应的内存上去分配。

(2)不要在.net程序中使用绑定线程到指定内核或提升线程优先级的做法,因为如果绑定的线程正好与垃圾回收线程进行竞争那么性能会更慢。

五、选择合适的编程模型

        编写并行程序都有固定的编程模型,基本上其他模型都是这几种模型的自由组合,常见的编程模型:

(1)数据并行

(2)任务并行

(3)流水线并行

六、去队列里拉数据还是队列主动推送数据?

         一般我们写多线程的程序会将数据先放到队列中,然后函数立即返回,后续再有其他的线程进行处理以达到快速响应客户端的目的,这就涉及到队列主动发送信号通知线程处理还是线程定时去队列里拉取数据,如果采用推送的方式可能造成状态丢失最终有些数据得不到处理而一直待在队列中,如果采用拉取的方式线程的睡眠时间不好把握,睡多了数据处理速度慢,睡少了又浪费CPU,所以我一般采用两者结合的方式,具体参考我的第四篇文章《.Net中的并行编程-4.实现高性能异步队列》

七、异步IO还是同步IO?

         异步IO可以解决同步IO的线程阻塞问题(这里的IO分两种:磁盘和网络),基本上所有的web服务器都采用的异步的网络IO,但是对于磁盘最好不要使用异步的磁盘IO,除非在具有SSD的机器上。因为对磁盘异步的读写会造成磁盘存储数据时的碎片化,本来可以顺序写的操作最终可能变成随机写。

      结束语:

本文设计的内容只是并行程序优化很少一部分,更多内容还需要我们在实践中不断积累。

本来想写的内容是”.net并行“,结果写完才发现和.net没有关系,当然,这些基础知识和语言没什么太大关系。

本文设计的内容只是建议,我们在编写程序时不要具有教条主义,合理的才是最好的,某些原则不是适合所有情况,我们所做的是不断探索适应变化以达到提升程序性能的目的。

.Net中的并行编程-6.常用优化策略的更多相关文章

  1. .Net中的并行编程-1.路线图(转)

    大神,大神,膜拜膜拜,原文地址:http://www.cnblogs.com/zw369/p/3834559.html 目录 .Net中的并行编程-1.路线图 分析.Net里线程同步机制 .Net中的 ...

  2. .Net中的并行编程-2.ConcurrentStack的实现与分析

    在上篇文章<.net中的并行编程-1.基础知识>中列出了在.net进行多核或并行编程中需要的基础知识,今天就来分析在基础知识树中一个比较简单常用的并发数据结构--.net类库中无锁栈的实现 ...

  3. .Net中的并行编程-3.ConcurrentQueue实现与分析

    在上文<.Net中的并行编程-2.ConcurrentQueue的实现与分析> 中解释了无锁的相关概念,无独有偶BCL提供的ConcurrentQueue也是基于原子操作实现, 由于Con ...

  4. .Net中的并行编程-5.流水线模型实战

    自己在Excel整理了很多想写的话题,但苦于最近比较忙(其实这是借口).... 上篇文章<.Net中的并行编程-4.实现高性能异步队列>介绍了异步队列的实现,本篇文章介绍我实际工作者遇到了 ...

  5. .Net中的并行编程-4.实现高性能异步队列

    上文<.Net中的并行编程-3.ConcurrentQueue实现与分析>分析了ConcurrentQueue的实现,本章就基于ConcurrentQueue实现一个高性能的异步队列,该队 ...

  6. Python中的并行编程速度

    这里主要想记录下今天碰到的一个小知识点:Python中的并行编程速率如何? 我想把AutoTool做一个并行化改造,主要目的当然是想提高多任务的执行速度.第一反应就是想到用多线程执行不同模块任务,但是 ...

  7. .Net中的并行编程-1.路线图

    最近半年一直研究用.net进行并行程序的开发与设计,再研究的过程中颇有收获,所以画了一个图总结了一下并行编程的基础知识点,这些知识点是并行编程的基础,有助于我们编程高性能的程序,里面的某些结构实现机制 ...

  8. PHP中的数据库一、MySQL优化策略综述

    前些天看到一篇文章说到PHP的瓶颈很多情况下不在PHP自身,而在于数据库.我们都知道,PHP开发中,数据的增删改查是核心.为了提升PHP的运行效率,程序员不光需要写出逻辑清晰,效率很高的代码,还要能对 ...

  9. .NET Framework 4 中的并行编程9---线程安全集合类

    原文转载自:http://www.cnblogs.com/xray2005/archive/2011/10/11/2206745.html 在.Net 4中,新增System.Collections. ...

随机推荐

  1. 仿SGI STL的traits技法

    首先是iterator traits,这个是用来萃取迭代器的特性的 #ifndef _STL_ITERATOR_H_ #define _STL_ITERATOR_H_ #include <cst ...

  2. spring bean生命周期管理--转

    Life Cycle Management of a Spring Bean 原文地址:http://javabeat.net/life-cycle-management-of-a-spring-be ...

  3. Elasticsearch入门必备——ES中的字段类型以及常用属性

    使用Elasticsearch时,了解字段的概念,是必不可少的.毕竟无论是es还是传统的数据库,都无法弱化字段的类型. 背景知识 在Es中,字段的类型很关键: 在索引的时候,如果字段第一次出现,会自动 ...

  4. Hyperledger中数据存取的实现

    简介 本文介绍了在Hyperledger中数据存取的实现. API接口 Hyperledger提供基于key/value的数据存储,其中key是字符串,value则是二进制字节数组,Hyperledg ...

  5. apache(nginx)+django+virutalenv(virtualenvwrapper)+gunicorn+supervisor配置高效web环境

    前言 django的调试模式配置简单,用于测试十分方便,但众所周知,这个只适合于调试,生产上运行效率十分低下. 后来考虑用nginx+uwsgi的模式进行,但之前配置过apache+wsgi的方式,感 ...

  6. ASP.NET MVC必知必会知识点总结(一)

    一.URL Routing 1.添加URL路由映射的一般方法(在RegisterRoutes方法中添加): //第一种(建议采用这种): routes.MapRoute( "MyRoute& ...

  7. Moon.Orm3.8技术全攻略

    Moon.ORM技术全攻略  一.绪论 本文主要是针对Moon.ORM的技术的讨论及其使用使用指导.如有其它疑问,请留言.本文主要针对Moon.ORM3.9版本,同时将会对4.0做一个技术预览.本文从 ...

  8. 利用bootstrap的modal组件自定义alert,confirm和modal对话框

    由于浏览器提供的alert和confirm框体验不好,而且浏览器没有提供一个标准的以对话框的形式显示自定义HTML的弹框函数,所以很多项目都会自定义对话框组件.本篇文章介绍自己在项目中基于bootst ...

  9. Dapper学习 - Dapper的基本用法(二) - 存储过程/函数

    上一篇貌似少介绍了自定义函数和存储过程, 因为这两个也可以使用查询的方式来实现功能, 这一篇就补上 一.自定义函数的创建和调用 (mysql的) Delimiter $$ drop function ...

  10. 两个dropDownList和一个GridView的选择与显示

    很久没有写ASP.NET了,今天有看到论坛上一个问题:"两个dropDownList和一个GridView,已经进行了数据绑定,现在想让第一个下拉菜单的数据改变时,第二个下拉菜单自动变到相应 ...