本文是.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. Warning: Null value is eliminated by an aggregate or other SET operation.

    Null 值会被聚合函数忽略,默认情况下,Sql Server会给出Warning: Warning: Null value is eliminated by an aggregate or othe ...

  2. 如何查看某个查询用了多少TempDB空间

        最近帮助客户调优的过程中,发现客户的TempDB存在非常大的压力,经过排查是发现某些语句对TempDB的巨量使用所导致.     在SQL Server中,TempDB主要负责供下述三类情况使 ...

  3. 移动web开发之视口viewport

    × 目录 [1]布局视口 [2]视觉视口 [3]理想视口[4]meta标签[5]总结 前面的话 在CSS标准文档中,视口viewport被称为初始包含块.这个初始包含块是所有CSS百分比宽度推算的根源 ...

  4. java中线程存活和线程执行的问题!

    /* 下面的程序会出现下面的情况,当Thread-0, Thread-1, Thread-2都被wait的时候,可能会同时苏醒 Thread-0 put Thread-1 put Thread-2 p ...

  5. Advice for students of machine learning--转

    原文地址:http://www.mimno.org/articles/ml-learn/ written by david mimno One of my students recently aske ...

  6. JavaWeb:实现文件上传

    JavaWeb:实现文件上传 理解文件上传: 1.上传文件就是把客户端的文件发送给服务器端. 2.HTTP响应的正文部分最常见的是HTML文档,但是也可以是其他任意格式的数据,如图片和声音文件中的数据 ...

  7. Anliven - 乱炖

    001 --- Ping Yourself! 由TCP/IP协议栈而想到的: 你的"协议分层"是如何的?有谁或者什么事务所对应着?谁先谁后,什么重要? 你的"协议栈&qu ...

  8. Concise - 面向对象的,一致的前端开发框架

    在当今世界,有许多前端开发的框架.那么,为什么还要再造一个框架呢?Concise 建立的目的是使你有很多的开箱即用的选项,让你能够方便的搭建移动友好的网站和 Web 应用程序.另外还包括一个简单的网格 ...

  9. Linux 启动过程分析

    本文仅简单介绍Linux的启动过程,在此基础上做简要的分析.对于Linux启动过程中内部详细的函数调用不做介绍,只是希望本文能给新手起到一个抛砖引玉的作用,以便深入研究Linux的启动过程.下图基本展 ...

  10. 【小型系统】抽奖系统-使用Java Swing完成

    一.需求分析 1. 显示候选人照片和姓名. 2. 可以使用多种模式进行抽奖,包括一人单独抽奖.两人同时抽奖.三人同时抽奖. 3. 一个人可以在不同的批次的抽奖中获取一.二.三等奖,但是不能在同一批次抽 ...