五千年(敝帚自珍)

主题:【原创】介绍一下Go语言(1)之前的话 -- zllwy

共:💬92 🌺231
分页树展主题 · 全看首页 上页
/ 7
下页 末页
      • 家园 instruction reordering

        不应该对执行结果有影响,否则这个优化就是错的了。另外硬件,特别是多CPU或者多核的架构,应该有自己在硬件层的cache coherency保证才是。就我所知,intel的CPU在多CPU时代就有专门的部分来保证跨CPU的内存一致性问题,到了多核时代,在memory bus以内,也有硬件来确保各个核心之间的缓存一致性。当然出了memory bus就没关系了,因为从memory bus看,里面有几个核心,对内存都是一样的。

      • 家园 有一些问题,比如那段synchronized程序

        语法都是错误的(你可以自己用Java Compiler验证一下),除去几处第一个字母不该大写这些小问题(估计你是copy/paste时候搞错的)。

        对synchronized原理解释的不是很贴切,对final的解释也不是很全面。因为final可以用于Class,method以及variable,你似乎只考虑了final变量的问题。

        final是个很有趣的东西,一方面被强烈推荐,另一方面又会出很多隐蔽的问题,等有空了再多说几句。

        • 家园 欢迎指正

          在word里面写的,很多格式都被弄乱了。代码只是示意性的。另外,主要是想说明多线程下这几个保留字的细微含义,并不是打算对他们本身进行解释。欢迎补充纠正。

    • 家园 【原创】介绍一下Go语言(4)并发机制

      先从著名的c10k问题谈起。有一个叫Dan Kegel的人在网上(http://www.kegel.com/c10k.html)提出:现在的硬件应该能够让一台机器支持10000个并发的client。然后他讨论了用不同的方式实现大规模并发服务的技术,归纳起来就是两种方式:一个client一个thread,用blocking I/O;多个clients一个thread,用nonblocking I/O或者asynchronous I/O。目前asynchronous I/O的支持在Linux上还不是很好,所以一般都是用nonblocking I/O。大多数的实现都是用epoll()的edge triggering(传统的select()有很大的性能问题)。这就引出了thread和event之争,因为前者就是完全用线程来处理并发,后者是用事件驱动来处理并发。当然实际的系统当中往往是混合系统:用事件驱动来处理网络时间,而用线程来处理事务。由于目前操作系统(尤其是Linux)和程序语言的限制(Java/C/C++等),线程无法实现大规模的并发事务。一般的机器,要保证性能的话,线程数量基本要限制几百(Linux上的线程有个特点,就是达到一定数量以后,会导致系统性能指数下降,参看SEDA的论文)。所以现在很多高性能web server都是使用事件驱动机制,比如nginx,Tornado,node.js等等。事件驱动几乎成了高并发的同义词,一时间红的不得了。

      其实线程和事件,或者说同步和异步之争早就在学术领域争了几十年了。1978年有人为了平息争论,写了论文证明了用线性的process(线程的模式)和消息传递(事件的模式)是等价的,而且如果实现合适,两者应该有同等性能。当然这是理论上的。针对事件驱动的流行,2003年加大伯克利发表了一篇论文叫“Why events are a bad idea (for high-concurrency servers)”,指出其实事件驱动并没有在功能上有比线程有什么优越之处,但编程要麻烦很多,而且特别容易出错。线程的问题,无非是目前的实现的原因。一个是线程占的资源太大,一创建就分配几个MB的stack,一般的机器能支持的线程大受限制。针对这点,可以用自动扩展的stack,创建的先少分点,然后动态增加。第二个是线程的切换负担太大,Linux中实际上process和thread是一回事,区别就在于是否共享地址空间。解决这个问题的办法是用轻量级的线程实现,通过合作式的办法来实现共享系统的线程。这样一个是切换的花费很少,另外一个可以维护比较小的stack。他们用coroutine和nonblocking I/O(用的是poll()+thread pool)实现了一个原型系统,证明了性能并不比事件驱动差。

      那是不是说明线程只要实现的好就行了呢。也不完全对。2006年还是加大伯克利,发表了一篇论文叫“The problem with threads”。线程也不行。原因是这样的。目前的程序的模型基本上是基于顺序执行。顺序执行是确定性的,容易保证正确性。而人的思维方式也往往是单线程的。线程的模式是强行在单线程,顺序执行的基础上加入了并发和不确定性。这样程序的正确性就很难保证。线程之间的同步是通过共享内存来实现的,你很难来对并发线程和共享内存来建立数学模型,其中有很大的不确定性,而不确定性是编程的巨大敌人。作者以他们的一个项目中的经验来说明,保证多线程的程序的正确性,几乎是不可能的事情。首先,很多很简单的模式,在多线程的情况下,要保证正确性,需要注意很多非常微妙的细节,否则就会导致deadlock或者race condition。其次,由于人的思维的限制,即使你采取各种消除不确定的办法,比如monitor,transactional memory,还有promise/future,等等机制,还是很难保证面面俱到。以作者的项目为例,他们有计算机科学的专家,有最聪明的研究生,采用了整套软件工程的流程:design review, code review, regression tests, automated code coverage metrics,认为已经消除了大多数问题,不过还是在系统运行4年以后,出现了一个deadlock。作者说,很多多线程的程序实际上存在并发错误,只不过由于硬件的并行度不够,往往不显示出来。随着硬件的并行度越来越高,很多原来运行完好的程序,很可能会发生问题。我自己的体会也是,程序NPE,core dump都不怕,最怕的就是race condition和deadlock,因为这些都是不确定的(non-deterministic),往往很难重现。

      那既然线程+共享内存不行,什么样的模型可以帮我们解决并发计算的问题呢。研究领域已经发展了一些模型,目前越来越多地开始被新的程序语言采用。最主要的一个就是Actor模型。它的主要思想就是用一些并发的实体,称为actor,他们之间的通过发送消息来同步。所谓“Don’t communicate by sharing memory, share memory by communicating”。Actor模型和线程的共享内存机制是等价的。实际上,Actor模型一般通过底层的thread/lock/buffer 等机制来实现,是高层的机制。Actor模型是数学上的模型,有理论的支持。另一个类似的数学模型是CSP(communicating sequential process)。早期的实现这些理论的语言最著名的就是erlang和occam。尤其是erlang,所谓的Ericsson Language,目的就是实现大规模的并发程序,用于电信系统。Erlang后来成为比较流行的语言。

      说到这里,比较理想的并发机制已经成型了。我们需要轻量级的并发实体(动态堆栈,用户空间的合作调度),需要类似Actor/CSP的消息传递机制。Go就是提供了这样的功能。Go的并发实体叫做goroutine,类似coroutine,但不需要自己调度。Runtime自己就会把goroutine调度到系统的线程上去运行,多个goroutine共享一个线程。如果有一个要阻塞,系统就会自动把其他的goroutine调度到其他的线程上去。Goroutine的堆栈最开始只有几十KB,可以动态在heap上增长。所以程序可以创建几千几万个goroutine。Goroutine的创建很简单:

      go foo()

      foo可以是一个函数调用,也可以是个closure。Goroutine通过channel来通信,channel可以是buffered,也可以是unbuffered。Buffered channel是异步的,unbuffered channel是同步的。Channel是first class type,可以被当作一个object传来传去,所以你可以把一个channel交给一个goroutine,让它通过这个channel把结果传回来。基本上,channel可以处理大部分goroutine同步的需要。Go当然也可以用底层的thread/lock之类的。有时候为了性能需要,可能也要用一下。

      元宝推荐:铁手, 通宝推:西电鲁丁,
      • 家园 可以分析一下python的twisted与go的比较
      • 家园 关于c10k,据说nginx解决了这个问题

        最近纠结于是继续用apache还是nginx。apache耗费内存的确是太大了,看着捧nginx好处的文章,还的确有些想试试nginx。就怕和当年的lightppd那样,火热了一下,发现毛病不少,结果现在落后了nginx许多。不过现在看势头,nginx似乎稳定性不错。

        关于go的并发,是语言上面容易写并发的程序,还是说它的系统可以把程序更容易并发话运行?

        • 家园 NGINX在国内很火,很多网站在用。

          还有反向代理(缓存)的功能,squid也省了。你既然用php,可以考虑用它。

        • 家园 更容易写并发程序

          Actor或者CSP主要是强调并发模型的简单,安全。和轻量级线程结合起来,还保证了并发性能和可扩展性。nginx之类解决了c10k的server都是用epoll()+event-driven。goroutine+channel理论上可以达到同样的性能但要简单很多。很多用Go的程序员的评价就是Go的程序要短很多。

          铁手如果用python的话,也可以考虑tornado,也是asynchronous web server,声称解决了c10k。

          自动对程序进行并行化是个不同的问题,很难做到比较精细的粒度。这方面有transactional memory等等。还在研究阶段。

          • 家园 不会python,不过最近势头比较猛

            所以也有些动心。眼下暂时是吊死在php上了。

            • 家园 python后劲不足

              最近势头最猛的最火热的是node.js了,长久以来,很多人就梦想要在server side跑js,随着node.js的火热,他们终于可以圆梦了 :P

              关键词(Tags): #node.js javascript
              • 家园 我搞不懂有什么特殊必要在服务器端跑js呢?

                听上去好像很有意思,但是千万别就是geek们的发烧玩物。不知道速度怎样?熟悉的话,要不就给个介绍吧。

                我现在暂时抱定观望态度。之前ruby on rails也很火热,现在感觉热度就不那么高了。

                • 家园 the point

                  理由是既然在客户端已经用js了,为什么不在服务器上也运行js呢?这样语言就统一了。至于性能,要感谢Google的V8 engine,我想理论上达到Java的水平应该是有可能的。网上随便搜了一下,好像比PHP好。

                  • 家园 js的语法还是会给编译器一点麻烦的

                    一样是object.field的语法,strong type的语言就可以直接编译成间址,js这样的就必须runtime去查找了。一样是转化成native code执行,间址还是比查找快的。

                    • 家园 这个有可能塞翁失马了

                      基于VM的东西,优化的空间有可能更大

                      没有vm的语言,比如C之流,全靠编译那个时候的一下子优化,一锤子买卖。有了VM,可以做jit,速度更快也不是不可能。

                      • 家园 没有完整的VM也可以优化的

                        动态优化不一定要一个完整的跟jvm一样的虚拟机。

                        如果单纯的按照字面意思做jit,把vm的指令改写成native code,那样其实不快多少的。更快的是做动态profiling,然后找hotspot优化。jvm在1.1就有jit了,但是到了1.2才做出hotspot jit来,那个才是性能可以和C这种有一拼的时候。

                        像C这种,如果编译的时候加了标记hotspot的代码,另外和动态优化库链接,那么还是可能做到动态优化的,而且不需要完整的VM

分页树展主题 · 全看首页 上页
/ 7
下页 末页


有趣有益,互惠互利;开阔视野,博采众长。
虚拟的网络,真实的人。天南地北客,相逢皆朋友

Copyright © cchere 西西河