包含结目的wwW622yucom页面升级,老是栏截622yu还出问题

&li&&strong&基础快捷键(+表示同时操作、-表示分步操作)&/strong&&/li&&br&&strong&Cmd+Shift+3:&/strong&全屏截图;截取整个屏幕,保存截图至桌面文件夹。&br&&strong&Cmd+Shift+4:&/strong&区域截图;鼠标光标变成带坐标的小十字,通过拖拽截取特定区域,保存截图至桌面文件夹。&br&&strong&Cmd+Shift+4 - 单击空格键 - 鼠标单击指定窗口:&/strong&窗口截图;出现小十字光标后对指定窗口按空格键,鼠标光标变成照相机,鼠标单击截取指定窗口,保存截图至桌面文件夹。&br&&li&&strong&进阶快捷键&/strong&&/li&&br&&strong&Cmd+Shift+4 - 按住空格键拖动鼠标:&/strong&区域截图;选取区域范围后,按住空格键并拖到鼠标可移动选取范围,释放按键后保存截图至桌面文件夹。&br&&strong&Cmd+Shift+4 - 按住Shift - 上下/左右移动鼠标:&/strong&区域截图;选取区域范围后,按住 Shift 并上下/左右移动鼠标即可固定选取范围宽度/高度改变高度/宽度,释放按键后保存截图至桌面文件夹。&br&&strong&Cmd+Shift+4 - 按住Shift和Option:&/strong& 区域截图;选取区域范围后,按住 Shift 和 Option 键在上一快捷键功能的基础上自由切换高度宽度,释放按键后保存截图至桌面文件夹。&br&&strong&Cmd+Shift+4 - 按住Option:&/strong& 区域截图;选取区域范围后,按住 Option 键可按比例缩放选取范围,释放按键后保存截图至桌面文件夹。&br&PS:以上介绍的快捷键配合按住 Control 键即可将截图文件保存在剪切板,以供调用。&br&&li&&strong&利用终端截图&/strong&&/li&&br&还有比较 Geek 的方法就是通过终端(Terminal)命令来截图,终端截图命令screencapture 有十多种参数可以选择,比如截图后立即显示、消除截图声音、延迟数秒后截图等。有兴趣的读者可以通过下面这条命令查看各个参数的具体使用方法。(运行结果即本文配图)&br&screencapture -h&br&系统自带截屏的技巧都在这里了,转自我在 &a href=&//link.zhihu.com/?target=http%3A//Tip4Mac.com& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&Tip4Mac.com&/span&&span class=&invisible&&&/span&&/a& 的一篇小贴士。也就是前面 &br&任平生 回答里面的那个链接:&a href=&//link.zhihu.com/?target=http%3A//www.tip4mac.com/2011/01/screencapture-for-os-x/& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://www.&/span&&span class=&visible&&tip4mac.com/2011/01/scr&/span&&span class=&invisible&&eencapture-for-os-x/&/span&&span class=&ellipsis&&&/span&&/a&
基础快捷键(+表示同时操作、-表示分步操作) Cmd+Shift+3:全屏截图;截取整个屏幕,保存截图至桌面文件夹。 Cmd+Shift+4:区域截图;鼠标光标变成带坐标的小十字,通过拖拽截取特定区域,保存截图至桌面文件夹。 Cmd+Shift+4 - 单击空格键 - 鼠标单击指定…
&p&房间里灯光昏暗,两个男人相对而坐,&/p&&p&良久,眼睛男率先打破僵局,&/p&&p&眼睛男,知道锁么&/p&&p&帅气男,知道些,&/p&&p&眼睛男:什么是锁?(先发制人)&/p&&figure&&img src=&https://pic4.zhimg.com/v2-84d373e5ed759bbd625a095d4a3c019b_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&446& data-rawheight=&400& class=&origin_image zh-lightbox-thumb& width=&446& data-original=&https://pic4.zhimg.com/v2-84d373e5ed759bbd625a095d4a3c019b_r.jpg&&&/figure&&p&&i&一种保护机制,在多线程的情况下,保证操作数据的正确性/一致性,&/i&&/p&&p&&br&&/p&&p&眼镜男:有哪几种分类?&/p&&p&&br&&/p&&p&&b&&i&悲观锁,乐观锁,独占锁,共享锁,公平锁,非公平锁,分布式锁,自旋锁&/i&&/b&&/p&&p&&br&&/p&&p&眼睛男:讲讲乐观锁悲观锁吧(顺藤摸瓜)&/p&&figure&&img src=&https://pic4.zhimg.com/v2-62ea5ce9d2dddd3f0a3bf_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&160& data-rawheight=&200& class=&content_image& width=&160&&&/figure&&p&&i&一般喜欢放在数据库来讲(其实这两个概念是属于计算机的,不要被误导),就说mysql吧,悲观锁,主要是表锁,行锁还有间隙锁,叶锁,读锁,因为这些锁在被触发的时候势必引起线程阻塞,所以叫悲观&/i&&/p&&p&&i&另外乐观锁其实在mysql本身中不存在的,但是mysql提供了种mvcc的机制,支持乐观锁机制,&/i&&/p&&p&&br&&/p&&p&眼睛男:&b&mvcc&/b&是咋回事?(诱敌深入)&/p&&figure&&img src=&https://pic3.zhimg.com/v2-bfb1aeed0f0baafa43062_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&246& data-rawheight=&261& class=&content_image& width=&246&&&/figure&&p&&i&只是在&b&innodb&/b&引擎下存在,mvcc是为了满足事务的&b&隔离&/b&,通过版本号的方式,避免同一数据不同事务间的竞争,所说的乐观锁只在事务级别为读未提交读提交,才会生效,&/i&&/p&&p&眼睛男:具体&b&mvcc&/b&机制有什么?(穷追不舍)&/p&&p&&i&多版本并发控制,保证数据操作在多线程过程中,保证事务隔离的机制,可以降低锁竞争的压力,保证比较高并发量,这个过程。在每开启一个事务时,会生成一个事务的版本号,被操作的数据会生成一条新的数据行(临时),但是在提交前对其他事务是不可见的,对于数据的更新操作成功,会将这个版本号更新到数据的行中,事务提交成功,将新的版本号,更新到此数据行(永久)中,这样保证了每个事务操作的数据,都是相互不影响的,也不存在锁的问题;&/i&&/p&&p&眼睛男:那么在多个事务(操作同一条数据)并发过程中,谁先成功?&/p&&p&&i&mysql判断,其实就是谁先提交成功算谁的&/i&&/p&&p&眼睛男:说到事务了,聊聊事务,&/p&&figure&&img src=&https://pic2.zhimg.com/v2-4bca34eda122b924ac53bcabdfecafdd_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&300& data-rawheight=&300& class=&content_image& width=&300&&&/figure&&p&&i&事务常说一系列操作作为一个整体要么都成功要么都失败,主要特性acid,事务的的实现主要依赖两个&b&log redo-log,undo-log&/b&,每次事务都会记录数据修改前的数据undo-log,修改后的数据放入redo-log,提出成功则使用&b&redo-log &/b&更新到磁盘,失败则使用&b&undo-log&/b&将数据恢复到事务之前的数据&/i&&/p&&p&&br&&/p&&p&眼镜男,嗯,再说说独占锁,共享锁吧(转移阵地)&/p&&p&(嗯,独占,共享,公平,非公平,自旋锁这些都是广泛的概念,很多语言都有,包括操作系统,js的同学请回避)&/p&&p&&i&独占锁很明显就是持锁的线程只能有一个,共享锁则可以有多个&/i&&/p&&p&&br&&/p&&p&眼睛男:独占可以理解,&b&共享&/b&的意义在哪里?&/p&&p&&br&&/p&&p&&i&共享锁是为了提高程序的效率,举个例子数据的操作有读写之分,对于写的操作加锁,保证数据正确性,而对于读的操作如果不加锁,在写读操作同时进行时,读的数据有可能不是最新数据,如果对读操作加独占锁,面对读多写少的程序肯定效率很低,所有就出现了共享锁,对于读的的操作就使用共享的概念,但是对于写的操作则是互斥的,保证了读写的数据操作都一致,在java中上述的锁叫读写锁&/i&&/p&&p&&br&&/p&&p&眼睛男:读写锁的机制是什么呢?(佯攻)&/p&&figure&&img src=&https://pic2.zhimg.com/v2-f7bc19a3f9_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&300& data-rawheight=&300& class=&content_image& width=&300&&&/figure&&p&&i&在java中&b&读写锁(ReadWritelock)&/b&的机制是基于&b&AQS&/b&的一种实现,保证读读共享,读写互斥,写写互斥,如果要说机制的话,还要从&b&AQS&/b&说起,这是java实现的一种锁机制,互斥锁,读者写锁,条件产量,信号量,栅栏的都是它的衍生物,主要工作基于&b&CHL队列&/b&,&b&voliate关键字修饰的状态符stat&/b&,线程去修改状态符成功了就是获取成功,失败了就进队列等待,等待唤醒,AQS中还有很重要的一个概念是&b&自旋&/b&,在等待唤醒的时候,很多时候会使用自旋(while(!cas()))的方式,不停的尝试获取锁,直到被其他线程获取成功&/i&&/p&&p&&i&共享与独占的区别就在于,&b&CHL队列&/b&中的节点的模式是EXCLUSIVE还是SHARED,当一个线程成功修改了&b&stat&/b&状态,表示获取了锁,如果线程所在的节点为SHARED,将开始一个读锁传递的过程,从头结点,向队列后续节点传递唤醒,直到队列结束或者遇到了EXCLUSIVE的节点,等待所有激活的读操作完成,然后进入到独享模式(这部分尽力了,大家还是看源码)&/i&&/p&&p&&i&公平与非公平的区别就在于线程第一次获取锁时,也就是执行修改stat操作时,是进队列还是直接修改状态,这是基本的工作机制,详细的估计可以再聊好几集&/i&&/p&&p&&br&&/p&&p&眼睛男:java 除了AQS 还有其他的锁支持么(佯攻未遂,寻找突破)&/p&&figure&&img src=&https://pic3.zhimg.com/v2-d87b4f1fa_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&500& data-rawheight=&500& class=&origin_image zh-lightbox-thumb& width=&500& data-original=&https://pic3.zhimg.com/v2-d87b4f1fa_r.jpg&&&/figure&&p&&i&在java中,&b&synchronized&/b&关键字,是语言自带的,也叫内置锁,&b&synchronized&/b&关键字,我们都知道被&b&synchronized&/b&修饰的方法或者代码块,在同一时间内,只允许一个线程执行,是明显的独享锁,&b&synchronized的实现机制?&/b&可以参考AQS的实现方式,只是AQS使用显示的用lock.lock()调用,而sync作为关键字修饰,你可以认为在synchronized修饰的地方,自动添加了lock方法,结束的地方进行了unlock释放锁的方法,只是被隐藏了,我们看不到。&/i&&/p&&p&&i&它本身实现有两部分:monitor对象,线程,工作机制还是线程抢占对象使用权,对象都有自己的对象头,存储了对象的很多信息,其中有一个是标识被哪个线程持有,对比AQS,线程从修改stat,变为修改&b&monitor&/b&的对象头,线程的等待区域动 AQS中的队列,变为monitor对象中的某个区域&/i&,&/p&&figure&&img src=&https://pic1.zhimg.com/v2-2b30a1b49fae04a7f120_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&240& data-rawheight=&240& class=&content_image& width=&240&&&/figure&&p&眼睛男:能细说么?(贴脸)&/p&&p&导演,他自己加戏&/p&&p&导演:按照剧本来&/p&&p&&i&锁一直是围绕线程安全来实现的,比如独占锁,它在内存里面的操作是怎么样的&/i&&/p&&p&&i&这个地方涉及到一个概念,内存模型(这个和jvm不要混淆,&b&The Java memory model used internally in the JVM divides memory between thread stacks and the heap. This diagram illustrates the Java memory model from a logic perspective&/b&),是JVM用来区别线程栈和堆的内存方式,每个线程在运行的时候,所操作的数据存储空间有两个,一个是&b&主内存&/b& 一个是&b&工作内存&/b&,主内存其实就是jvm中堆,工作内存就是线程的栈,每次的数据操作,都是从主内存中把数据读到工作内存中,然后在工作内存中进行各种处理,如果进行了修改,会把数据回写到主内存,然后其他线程又进行同样的操作,就这样数据在工作内存和主内存,进进出出,不亦乐乎,在多次的情况下,就是因为进进出出的顺序乱了,不是按照线程预期的访问顺序,就出现了数据不一致的问题,导致了多线程的不安全性;整个操作过程还牵涉到&b&CPU,高速缓存&/b&等概念,略过。。。。&/i&&/p&&p&&br&&/p&&p&眼睛男:内存模型 还有哪些可以聊聊的(我们是抱着学习的心态)&/p&&p&&br&&/p&&p&&i&&b&happen-befor&/b& 原则,&b&Volatile&/b& 关键字(线程的可见性)&b&,内存屏障&/b&&/i&&/p&&p&&br&&/p&&p&眼睛男:哦(怂了怂了)&/p&&figure&&img src=&https://pic4.zhimg.com/v2-b5ecbfd8ddd0a26f00f7a3_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&431& data-rawheight=&261& class=&origin_image zh-lightbox-thumb& width=&431& data-original=&https://pic4.zhimg.com/v2-b5ecbfd8ddd0a26f00f7a3_r.jpg&&&/figure&&p&&i&happen-befor原则定义了内存模型执行过程中的定律,就像1+1 = 2,不可能被打破的jvm的运行机制都依赖于这个原则,是jvm的宪法!!!&/i&&/p&&p&&i&Volatile关键字就有点叼了,Volatile修饰的数据,在被某个线程修改后,会被及时的回写到主内存,然后其他线程再获取时,就是新的数据,听起来很美好,但是Volatile没有办法控制线程的顺序,当一个数据(新数据)即将被修改到主内存时,刚好,另外一个线程从主内存读了数据(老数据),并又进行了一波操作,又将数据(更新的数据)回写到了主内存,整个过程(新数据)完全没有起到一毛钱作用,最终导致了数据的错误,呼呼打完收工!!!!&/i&&/p&&figure&&img src=&https://pic2.zhimg.com/v2-78f23c1fce6497feb03ccfeb423fbd45_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&180& data-rawheight=&180& class=&content_image& width=&180&&&/figure&&p&眼镜男:你为啥知道这么多&/p&&p&&i&因为我帅啊,&/i&&/p&&p&眼镜男:有多帅?&/p&&p&&i&可以用微笑杀死你&/i&&/p&&p&眼镜男:来啊!&/p&&figure&&img src=&https://pic3.zhimg.com/v2-5c6e394cb9fe3de7ce430df8a10bf062_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&165& data-rawheight=&169& class=&content_image& width=&165&&&/figure&&p&眼睛男猝 享年28岁!!!&/p&&p&&br&&/p&&p&。。。。。end。。。。。。&/p&&p&&br&&/p&&p&整个文章以“锁”为半径,构建了一个简单的知识体系,直观的感受一下&/p&&figure&&img src=&https://pic1.zhimg.com/v2-26d8cff72df_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1920& data-rawheight=&1080& class=&origin_image zh-lightbox-thumb& width=&1920& data-original=&https://pic1.zhimg.com/v2-26d8cff72df_r.jpg&&&/figure&&p&更详细的请打开&u&&a href=&http://link.zhihu.com/?target=http%3A//treenpool.com/html/index.html%3FbranchId%3D488& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&treenpool.com/html/inde&/span&&span class=&invisible&&x.html?branchId=488&/span&&span class=&ellipsis&&&/span&&/a&&/u&&/p&&p&&u&本文旨在为大家扩宽思路,认识一下不太常见的概念,不要太纠结于细节!!!(怂了怂了)&/u&&/p&&p&这是以点及面比较好的例子,从一个点不断深入,你会发现很多新的东西,而这些就是需要一点一滴咀嚼的,看博客或者视频都是经过别人嚼烂消化过的东西,然后再吐出来展示给大家,大家看看他吃的啥就行,千万不要再吃下去,因为真正的养分已经不多了,还是需要从书啃起,一口口嚼出来,经过味蕾,大脑,胃,大肠。。。。一点点吸收,才是自己的,这也是我个人平时学习的方法,也祝愿大家可以用自己的方法,不断提升自己,扩展自己的知识体系;;;&/p&&p&本文很多描述的不是很完全,毕竟每个点随随便便就是几千字的,本人能力有限,只能把最近吃的吐出来给大家看看,不知道是否符合大家的胃口,&/p&&p&好书推荐,很多同学希望可以推荐一下书,我整理了一下,以下几类&/p&&p&入门级&/p&&p&&b&java核心编程卷1&/b&:&u&&a href=&http://link.zhihu.com/?target=http%3A//url.cn/5XFpwDI& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&url.cn/5XFpwDI&/span&&span class=&invisible&&&/span&&/a&&/u&,很多入门的同学,想要推荐一下书,之前想推荐thinking in java,不过对比下,这本书更加实用,可以作为入门的工具书&/p&&p&&b&java核心编程卷2&/b&:&u&&a href=&http://link.zhihu.com/?target=http%3A//url.cn/51VMnA6& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&url.cn/51VMnA6&/span&&span class=&invisible&&&/span&&/a&&/u& 目前只有英文版的,篇幅比较长,对java的一些高级特效比如线程 网络之类的感兴趣可以看下,里面的AWT就不要看了,基本已经绝迹江湖了&/p&&p&&b&python核心编程&/b&:&u&&a href=&http://link.zhihu.com/?target=http%3A//url.cn/5eMWKpL& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&url.cn/5eMWKpL&/span&&span class=&invisible&&&/span&&/a&&/u& ,Python的 入门工具书&/p&&p&进阶级&/p&&p&&b&java 设计模式&/b& &u&&a href=&http://link.zhihu.com/?target=http%3A//url.cn/5w5BSV2& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&url.cn/5w5BSV2&/span&&span class=&invisible&&&/span&&/a&&/u&,我买的第一本正儿八经的书,当时经常拿来吹牛&/p&&p&&b&effective java&/b&:&u&&a href=&http://link.zhihu.com/?target=http%3A//url.cn/5qNRfaC& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&url.cn/5qNRfaC&/span&&span class=&invisible&&&/span&&/a&&/u&,这是英文版的,和我买的一样,每章都很独立,有很多奇思妙想(奇技淫巧)&/p&&p&&b&深入理解Java虚拟机-JVM高级特性与最佳实践&/b&:&u&&a href=&http://link.zhihu.com/?target=http%3A//url.cn/5Jwsuf6& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&url.cn/5Jwsuf6&/span&&span class=&invisible&&&/span&&/a&&/u& ,这本书我读了差不多5编,很多jvm的点都能体现到,想要深入了解jvm的同学不要错过&/p&&p&&b&Java并发编程:&/b& &u&&a href=&http://link.zhihu.com/?target=http%3A//url.cn/5xyxETT& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&url.cn/5xyxETT&/span&&span class=&invisible&&&/span&&/a&&/u& 应该也有5遍,里面对于线程安全的东西讲的很透彻,如果想结合JMM和jvm了解并发的同学,深入理解jvm和java并发编程是绝配,&/p&&p&&br&&/p&&p&内核型书籍:&/p&&p&redis设计与实现:&u&&a href=&http://link.zhihu.com/?target=http%3A//url.cn/5TpE4n9& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&url.cn/5TpE4n9&/span&&span class=&invisible&&&/span&&/a&&/u&,之前对redis只是会用,甚至一直认为,redis的存储也是以树为基础,结果,被打脸,适合使用过redis的同学&/p&&p&&br&&/p&&p&linux内核设计与实现:&u&&a href=&http://link.zhihu.com/?target=http%3A//url.cn/55hIhMB& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&url.cn/55hIhMB&/span&&span class=&invisible&&&/span&&/a&&/u&,适合所有人,你会发现很多语言性的概念都是抄袭自linux&/p&&p&http权威指南:&u&&a href=&http://link.zhihu.com/?target=http%3A//url.cn/5Y731VY& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&url.cn/5Y731VY&/span&&span class=&invisible&&&/span&&/a&&/u&,一本手册,这么厚,现在依然觉得,谁读完谁傻逼,不过里面的一些章节需要好好琢磨,比如第三章 HTTP报文 第四章连接管理
第七章缓存 第九章, 第十一章 客户端识别与cookie机制 第十二章基本认证机制 第十二章。。。算了,还是尽量读完吧&/p&&p&&br&&/p&&p&高性能&u&&a href=&http://link.zhihu.com/?target=http%3A//url.cn/512yVHi& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&url.cn/512yVHi&/span&&span class=&invisible&&&/span&&/a&&/u& 应该有三遍,里面的对于mysql的数据结构讲述的比较清楚,但是不是很全,在锁和索引的描述篇幅比较多,以及一些高级特性 分区,事物,视图,值得一看&/p&&p&&br&&/p&&p&体系型:&/p&&p&大型网站系统与Java中间件实践:&u&&a href=&http://link.zhihu.com/?target=http%3A//url.cn/5jvxxfQ& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&url.cn/5jvxxfQ&/span&&span class=&invisible&&&/span&&/a&&/u&&/p&&p&大型分布式网站架构设计与实践 :&u&&a href=&http://link.zhihu.com/?target=http%3A//url.cn/51XEcwk& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&url.cn/51XEcwk&/span&&span class=&invisible&&&/span&&/a&&/u&&/p&&p&这两本书对于工作了4-5年的同学会有很大的启发,是突破瓶颈的好书&/p&&p&&br&&/p&&p&最后!!!高能预警&/p&&figure&&img src=&https://pic3.zhimg.com/v2-66aa91e07c2cfcb1ba47c2_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&534& data-rawheight=&300& class=&origin_image zh-lightbox-thumb& width=&534& data-original=&https://pic3.zhimg.com/v2-66aa91e07c2cfcb1ba47c2_r.jpg&&&/figure&&p&&br&&/p&&p&欢迎大家加微信骚扰:treenpool&/p&&p&关注微信公众号:treenpool,更多好文在里面&/p&&p&请持续关注,&a href=&http://link.zhihu.com/?target=http%3A//treenpool.com& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&treenpool.com&/span&&span class=&invisible&&&/span&&/a&&/p&
房间里灯光昏暗,两个男人相对而坐,良久,眼睛男率先打破僵局,眼睛男,知道锁么帅气男,知道些,眼睛男:什么是锁?(先发制人)一种保护机制,在多线程的情况下,保证操作数据的正确性/一致性, 眼镜男:有哪几种分类? 悲观锁,乐观锁,独占锁,共享锁,…
&figure&&img src=&https://pic2.zhimg.com/v2-fb3e0ad7a6be9_b.jpg& data-rawwidth=&640& data-rawheight=&339& class=&origin_image zh-lightbox-thumb& width=&640& data-original=&https://pic2.zhimg.com/v2-fb3e0ad7a6be9_r.jpg&&&/figure&&p&面试中,为了考察应聘者的思维方式,面试官偶尔会出一些谜题(Puzzles)。&/p&&p&比如,在谷歌,就有这样一道让人“闻风丧胆”的面试题:&/p&&p&&br&&/p&&blockquote&You work in a 100 floor building and you get 2 identical eggs. &br&You need to figure out the highest floor an egg can be dropped without breaking. &br&The question is how many throws you need to make.&br&Find an algorithm that is minimizing number of throws in the worst-case scenario.&/blockquote&&p&&br&&/p&&p&这道题的意思大致如下:&/p&&p&&b&&u&情景假设 &/u&&/b&&/p&&p&1、你在一个&b&100层高的大楼里&/b&&/p&&p&2、你有&b&2个一模一样的鸡蛋&/b&&/p&&p&&br&&/p&&p&&b&&u&任务 &/u&&/b&&/p&&p&1、弄清楚:&b&鸡蛋最高可以从几层楼扔下去而不会被摔坏&/b&&/p&&p&2、弄清楚你需要扔几次&/p&&p&3、&b&提出一个算法,找出在最坏情况下,扔出鸡蛋而不把鸡蛋摔坏的最少次数&/b&&/p&&p&&br&&/p&&figure&&img src=&https://pic1.zhimg.com/v2-684ac55cef1a_b.jpg& data-caption=&& data-size=&small& data-rawwidth=&616& data-rawheight=&922& class=&origin_image zh-lightbox-thumb& width=&616& data-original=&https://pic1.zhimg.com/v2-684ac55cef1a_r.jpg&&&/figure&&p&&br&&/p&&p&好在近日,一位来自Gamekit的Senior Android Developer,在网上分享了他的解法。对去Google工作感兴趣的小伙伴们,就赶紧来看看他是怎么说的吧:&/p&&p&&br&&/p&&h2&&b&假设&/b&&/h2&&p&首先,在解题之前,我们要做好几个简单的假设:&/p&&ul&&li&2个鸡蛋的脆弱程度是一样的。&/li&&li&如果鸡蛋从N楼扔下来没有碎,那么它从小于N楼扔下来,也不会碎&/li&&li&如果鸡蛋从N楼扔下来碎了,那么它从大于N楼扔下来,也一定会碎&/li&&li&一颗扔出去但没有碎的鸡蛋,可以继续被用于试验。&/li&&li&碎了的鸡蛋将无法再继续试验。&/li&&/ul&&p&有了这些假设之后,我们就可以解题了。 其实解决这道问题的方法有很多,在此列举一些:&/p&&p&&br&&/p&&h2&&b&最简单解法&/b&&/h2&&p&最简单的一个方法,就是&b&将鸡蛋从第一层开始,逐层扔出,看它在哪一层会摔碎。&/b&&/p&&p&这个方法虽然可靠,但它可能需要进行很多次尝试。比如,在最差的情况下,它需要尝试的次数是100次。&/p&&p&需要注意的一点是,当你只有一颗鸡蛋时,这个算法是唯一可靠的方法。&/p&&p&所以一旦你打碎了第一颗鸡蛋,手里只剩下最后一颗的时候,你就要开始运用这个算法。&/p&&p&&br&&/p&&h2&&b&最直观解法&/b&&/h2&&p&这个方法重点,是要&b&利用好第一颗鸡蛋,最大效率地把100层高楼划分成N个更小数目的区间。&/b&一个比较直观和流行的答案是,&b&将鸡蛋从【要检查的楼层* 1/N】层开始扔下去,逐层检查。&/b&&/p&&p&例如,当N=3时,我们就将第一颗鸡蛋从100*1/3 ≈ 33层楼扔出:&/p&&p&? 如果它破损了,我们就接着用第二颗鸡蛋检验32层楼及以下。&/p&&p&? 如果它没破损,我们就继续将同一颗鸡蛋从33 + (67 * 1/3) = 55层楼扔出,如果它破了,我们就用第二颗鸡蛋,检验34层 - 55层&/p&&p&......&/p&&p&当N = 3时,最坏的情况是max(33, 24, …) = 33层。&/p&&p&按照这个思路,再通过dynamic programming,我们就可以找到一个完美的N,来最优化鸡蛋的投掷次数了。&/p&&p&这个解法在面试中还是很有“价值”的,毕竟它能向面试官展现求职者的编程思维。但它还不是最佳解法。&/p&&p&&br&&/p&&h2&&b&最优解&/b&&/h2&&p&在理解最佳解法之前,我们需要理解以下这个equilibrium(均衡状态):&/p&&figure&&img src=&https://pic1.zhimg.com/v2-62fe6e026dbb25bcc930f3d_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&380& data-rawheight=&18& class=&content_image& width=&380&&&/figure&&p&这个均衡状态计算的是在最坏情境下,所需的扔鸡蛋次数。这里,F(n)指的是楼层,是我们扔第一个鸡蛋后的下一层。&/p&&p&假如我们引入以下变量:&/p&&figure&&img src=&https://pic3.zhimg.com/v2-d0190e45bafc3c72d3c77cd0d16908dc_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&172& data-rawheight=&18& class=&content_image& width=&172&&&/figure&&p&那么刚刚的equilibrium就变成这样:&/p&&figure&&img src=&https://pic4.zhimg.com/v2-37afa60dfcb1fb364e4972a_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&280& data-rawheight=&18& class=&content_image& width=&280&&&/figure&&p&&b&当这个函数里的所有参数都相等时,就是我们的最优解。&/b&&/p&&p&那么我们如何做到呢?&/p&&p&从末尾开始看,最后的D(n)将会变成1,因为最终我们将会到达一个点 —— 就是只剩下一层楼可以扔第一颗鸡蛋。&/p&&p&因此,D(n-1)应该等于2。&/p&&p&我们接着会发现,第一颗鸡蛋最终应该是从第99层楼扔出,之前是从99-2=97层,再往前则是97-3=94层,90, 85, 79, 72, 64, 55, 45, 34, 22,然后是第9层。&/p&&p&这就是一个最优解!&/p&&p&这样一来,我们就得出了答案:&/p&&p&&b&在最坏的情况下,我们需要的扔鸡蛋的最少次数,是14次&/b&(最小的差别在于13,但是我们还需要在第9层额外扔一次)。&/p&&p&&br&&/p&&h2&&b&检查&/b&&/h2&&p&现在就到了检验我们的解法是否正确的时候了。我们可以编写一个简单的Kotlin程序来检验答案。首先,我们需要解释一下,如何在某些决策中,计算扔鸡蛋次数。当有2层或更少的楼层时,我们需要按照剩余的楼层数,来决定扔鸡蛋的次数。&/p&&p&否则,我们应该调用以下均衡函数:&/p&&figure&&img src=&https://pic1.zhimg.com/v2-293dbbfc9c95543c6ddcfe_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&640& data-rawheight=&278& class=&origin_image zh-lightbox-thumb& width=&640& data-original=&https://pic1.zhimg.com/v2-293dbbfc9c95543c6ddcfe_r.jpg&&&/figure&&p&我们在这里使用了bestMaxThrows函数。这是一个假设的函数,它会返回一个投掷次数的数值,并假设接下来的一系列决策是完美的。&/p&&p&我们是这样定义它的:&/p&&figure&&img src=&https://pic3.zhimg.com/v2-ddafbb984ca_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&640& data-rawheight=&210& class=&origin_image zh-lightbox-thumb& width=&640& data-original=&https://pic3.zhimg.com/v2-ddafbb984ca_r.jpg&&&/figure&&p&同样,我们把“计算下一层最优解”的任务,交给了bestNextStep 函数。这个函数很好的为我们指明了下一步的方向。我们可以这样简单地定义它:当只有二层或更少的楼层待检验时,我们会从第一层扔出鸡蛋。否则,我们需要检查所有备选项,然后找到最优解。&/p&&p&下面是具体执行步骤:&/p&&figure&&img src=&https://pic2.zhimg.com/v2-85cb10ec11c2c4caaab38b365c6097cb_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&640& data-rawheight=&313& class=&origin_image zh-lightbox-thumb& width=&640& data-original=&https://pic2.zhimg.com/v2-85cb10ec11c2c4caaab38b365c6097cb_r.jpg&&&/figure&&p&需要注意的是,这个方程用了maxThrows函数,因此会涉及到recurrence (循环)。&/p&&p&但这并不成问题,因为当bestNextStep调用maxThrows时,它始终会使用比floorLeft更小的值调用它(因为nextFloor总是大于0)。&/p&&p&在我们使用它之前,我们需要添加一些缓冲,从而加速计算:&/p&&figure&&img src=&https://pic4.zhimg.com/v2-7fc4be7ac4a911b2a8b787_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&640& data-rawheight=&916& class=&origin_image zh-lightbox-thumb& width=&640& data-original=&https://pic4.zhimg.com/v2-7fc4be7ac4a911b2a8b787_r.jpg&&&/figure&&p&首先,我们可以检查它是否返回与我们计算结果相同的结果:&/p&&figure&&img src=&https://pic1.zhimg.com/v2-f080b2fc51ffe_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&640& data-rawheight=&202& class=&origin_image zh-lightbox-thumb& width=&640& data-original=&https://pic1.zhimg.com/v2-f080b2fc51ffe_r.jpg&&&/figure&&p&14 —— 这个结果看上去还不错,我们接着检查之后的几个步骤:&/p&&figure&&img src=&https://pic2.zhimg.com/v2-31eb43f9db6_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&640& data-rawheight=&396& class=&origin_image zh-lightbox-thumb& width=&640& data-original=&https://pic2.zhimg.com/v2-31eb43f9db6_r.jpg&&&/figure&&p&&br&&/p&&h2&&b&Result&/b&&/h2&&p&9, 22, 34, 45, 55, 64, 72, 79, 85, 90, 94, 97, 99, 100,&/p&&p&&b&跟我们计算的结果是一致的!&/b&&/p&&p&&br&&/p&&h2&&b&Bigger picture&/b&&/h2&&p&以上分享的这个算法,其实还可以解决许多其他类似的问题。&/p&&p&比如,我们可以把原题的提问改改,改成计算最随机情况下的扔鸡蛋次数。我们还可以看看,当建筑物的高度有变化时,得出的结果是否也会跟着变化。&/p&&p&下图很好地回答了以上问题:&/p&&figure&&img src=&https://pic3.zhimg.com/v2-c604f06530fac454dd2a1a54dc806ade_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&640& data-rawheight=&432& class=&origin_image zh-lightbox-thumb& width=&640& data-original=&https://pic3.zhimg.com/v2-c604f06530fac454dd2a1a54dc806ade_r.jpg&&&/figure&&p&希望这些对正在求职的你们,有所帮助。&/p&&p&&br&&/p&&blockquote&编译自:&a href=&https://link.zhihu.com/?target=https%3A//medium.freecodecamp.org/how-to-solve-the-google-recruiters-puzzle-about-throwing-eggs-from-a-building-de6e7ef1755d& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&medium.freecodecamp.org&/span&&span class=&invisible&&/how-to-solve-the-google-recruiters-puzzle-about-throwing-eggs-from-a-building-de6e7ef1755d&/span&&span class=&ellipsis&&&/span&&/a&&br&本文不代表来Offer立场,版权属于原作者所有。&/blockquote&&hr&&p&&b&来Offer软件工程师旗舰核心课程3月8日开班,第一节课免费试听!&/b&请微信添加来Offer课程顾问:&b&laiofferhelper2&/b& 为好友进行咨询。&/p&&p&了解更多北美CS求职信息,欢迎关注我们的官方微信号:&b&来Offer网&/b&&/p&
面试中,为了考察应聘者的思维方式,面试官偶尔会出一些谜题(Puzzles)。比如,在谷歌,就有这样一道让人“闻风丧胆”的面试题: You work in a 100 floor building and you get 2 identical eggs. You need to figure out the highest floor an egg can b…
&figure&&img src=&https://pic4.zhimg.com/v2-2d883d38b6daca154ed626_b.jpg& data-rawwidth=&550& data-rawheight=&550& class=&origin_image zh-lightbox-thumb& width=&550& data-original=&https://pic4.zhimg.com/v2-2d883d38b6daca154ed626_r.jpg&&&/figure&&p&原文: &b&&i&&a href=&https://link.zhihu.com/?target=https%3A//www.toptal.com/back-end/server-side-io-performance-node-php-java-go& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Server-side I/O Performance: Node vs. PHP vs. Java vs. Go&/a&&/i&&/b&&/p&&p&作者:BRAD PEABODY&/p&&p&翻译:雁惊寒&/p&&p&摘要:本文首先简单介绍了I/O相关的基础概念,然后横向比较了Node、PHP、Java、Go的I/O性能,并给出了选型建议。以下是译文。&/p&&p&了解应用程序的输入/输出(I/O)模型能够更好的理解它在处理负载时理想情况与实际情况下的差异。也许你的应用程序很小,也无需支撑太高的负载,所以这方面需要考虑的东西还比较少。但是,随着应用程序流量负载的增加,使用错误的I/O模型可能会导致非常严重的后果。&/p&&figure&&img src=&https://pic1.zhimg.com/v2-cfc77fa17c4cbeab62290e_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&550& data-rawheight=&550& class=&origin_image zh-lightbox-thumb& width=&550& data-original=&https://pic1.zhimg.com/v2-cfc77fa17c4cbeab62290e_r.jpg&&&/figure&&p&在本文中,我们将把Node、Java、Go和PHP与Apache配套进行比较,讨论不同语言如何对I/O进行建模、每个模型的优缺点,以及一些基本的性能评测。如果你比较关心自己下一个Web应用程序的I/O性能,本文将为你提供帮助。&/p&&p&I/O基础:快速回顾一下&/p&&p&要了解与I/O相关的因素,我们必须首先在操作系统层面上了解这些概念。虽然不太可能一上来就直接接触到太多的概念,但在应用的运行过程中,不管是直接还是间接,总会遇到它们。细节很重要。&/p&&p&系统调用&/p&&p&首先,我们来认识下系统调用,具体描述如下:&/p&&ul&&li&应用程序请求操作系统内核为其执行I/O操作。&/li&&li&“系统调用”是指程序请求内核执行某些操作。其实现细节因操作系统而异,但基本概念是相同的。在执行“系统调用”时,将会有一些控制程序的特定指令转移到内核中去。一般来说,系统调用是阻塞的,这意味着程序会一直等待直到内核返回结果。&/li&&li&内核在物理设备(磁盘、网卡等)上执行底层I/O操作并回复系统调用。在现实世界中,内核可能需要做很多事情来满足你的请求,包括等待设备准备就绪、更新其内部状态等等,但作为一名应用程序开发人员,你无需关心这些,这是内核的事情。&/li&&/ul&&figure&&img src=&https://pic4.zhimg.com/v2-363c2c5c1a_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&550& data-rawheight=&550& class=&origin_image zh-lightbox-thumb& width=&550& data-original=&https://pic4.zhimg.com/v2-363c2c5c1a_r.jpg&&&/figure&&p&阻塞调用与非阻塞调用&/p&&p&我在上面说过,系统调用一般来说是阻塞的。但是,有些调用却属于“非阻塞”的,这意味着内核会将请求放入队列或缓冲区中,然后立即返回而不等待实际I/O的发生。所以,它只会“阻塞”很短的时间,但排队需要一定的时间。&/p&&p&为了说明这一点,下面给出几个例子(Linux系统调用):&/p&&ul&&li&&code&read()&/code& 是一个阻塞调用。我们需要传递一个文件句柄和用于保存数据的缓冲区给它,当数据保存到缓冲区之后返回。它的优点是优雅而又简单。&/li&&li&&code&epoll_create()&/code& 、 &code&epoll_ctl()&/code& 和 &code&epoll_wait()&/code& 可用于创建一组句柄进行监听,添加/删除这个组中的句柄、阻塞程序直到句柄有任何的活动。这些系统调用能让你只用单个线程就能高效地控制大量的I/O操作。这些功能虽然非常有用,但使用起来相当复杂。&/li&&/ul&&p&了解这里的时间差的数量级非常重要。如果一个没有优化过的CPU内核以3GHz的频率运行,那么它可以每秒执行30亿个周期(即每纳秒3个周期)。一个非阻塞的系统调用可能需要大约10多个周期,或者说几个纳秒。对从网络接收信息的调用进行阻塞可能需要更长的时间,比如说200毫秒(1/5秒)。比方说,非阻塞调用花了20纳秒,阻塞调用花了200,000,000纳秒。这样,进程为了阻塞调用可能就要等待1000万个周期。&/p&&figure&&img src=&https://pic1.zhimg.com/v2-14b0d30a38b53e1df61eec26_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&550& data-rawheight=&550& class=&origin_image zh-lightbox-thumb& width=&550& data-original=&https://pic1.zhimg.com/v2-14b0d30a38b53e1df61eec26_r.jpg&&&/figure&&p&内核提供了阻塞I/O(“从网络读取数据”)和非阻塞I/O(“告诉我网络连接上什么时候有新数据”)这两种方法,并且两种机制阻塞调用进程的时间长短完全不同。&/p&&p&调度&/p&&p&第三个非常关键的事情是当有很多线程或进程开始出现阻塞时会发生什么问题。&/p&&p&对我们而言,线程和进程之间并没有太大的区别。而在现实中,与性能相关的最显著的区别是,由于线程共享相同的内存,并且每个进程都有自己的内存空间,所以单个进程往往会占用更多的内存。但是,在我们谈论调度的时候,实际上讲的是完成一系列的事情,并且每个事情都需要在可用的CPU内核上获得一定的执行时间。如果你有8个内核来运行300个线程,那么你必须把时间分片,这样,每个线程才能获得属于它的时间片,每一个内核运行很短的时间,然后切换到下一个线程。这是通过“上下文切换”完成的,可以让CPU从一个线程/进程切换到下一个线程/进程。&/p&&p&这种上下文切换有一定的成本,即需要一定的时间。快的时候可能会小于100纳秒,但如果实现细节、处理器速度/架构、CPU缓存等软硬件的不同,花个1000纳秒或更长的时间也很正常。&/p&&p&线程(或进程)数量越多,则上下文切换的次数也越多。如果存在成千上万的线程,每个线程都要耗费几百纳秒的切换时间的时候,系统就会变得非常慢。&/p&&p&然而,非阻塞调用实质上告诉内核“只有在这些连接上有新的数据或事件到来时才调用我”。这些非阻塞调用可有效地处理大I/O负载并减少上下文切换。&/p&&p&值得注意的是,虽然本文举得例子很小,但数据库访问、外部缓存系统(memcache之类的)以及任何需要I/O的东西最终都会执行某种类型的I/O调用,这跟示例的原理是一样的。&/p&&p&影响项目中编程语言选择的因素有很多,即使你只考虑性能方面,也存在很多的因素。但是,如果你担心自己的程序主要受I/O的限制,并且性能是决定项目成功或者失败的重要因素,那么,下文提到的几点建议就是你需要重点考虑的。&/p&&p&“保持简单”:PHP&/p&&p&早在上世纪90年代,有很多人穿着 &b&&i&&a href=&https://link.zhihu.com/?target=https%3A//www.pinterest.com/pin/852181/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Converse&/a&&/i&&/b& 鞋子使用Perl编写CGI脚本。然后,PHP来了,很多人都喜欢它,它使得动态网页的制作更加容易。&/p&&p&PHP使用的模型非常简单。虽然不可能完全相同,但一般的PHP服务器原理是这样的:&/p&&p&用户浏览器发出一个HTTP请求,请求进入到Apache web服务器中。 Apache为每个请求创建一个单独的进程,并通过一些优化手段对这些进程进行重用,从而最大限度地减少原本需要执行的操作(创建进程相对而言是比较慢的)。&/p&&p&Apache调用PHP并告诉它运行磁盘上的某个 &code&.php&/code& 文件。&/p&&p&PHP代码开始执行,并阻塞I/O调用。你在PHP中调用的 &code&file_get_contents()&/code& ,在底层实际上是调用了 &code&read()&/code& 系统调用并等待返回的结果。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&&?php// blocking file I/O$file_data = file_get_contents(‘/path/to/file.dat’);
// blocking network I/O$curl = curl_init('http://example.com/example-microservice');
$result = curl_exec($curl);
// some more blocking network I/O$result = $db-&query('SELECT id, data FROM examples ORDER BY id DESC limit 100');
&/code&&/pre&&/div&&p&与系统的集成示意图是这样的:&/p&&figure&&img src=&https://pic4.zhimg.com/v2-db03add47d5cf3abf198c12947ccc8ca_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&550& data-rawheight=&550& class=&origin_image zh-lightbox-thumb& width=&550& data-original=&https://pic4.zhimg.com/v2-db03add47d5cf3abf198c12947ccc8ca_r.jpg&&&/figure&&p&很简单:每个请求一个进程。 I/O调用是阻塞的。那么优点呢?简单而又有效。缺点呢?如果有20000个客户端并发,服务器将会瘫痪。这种方法扩展起来比较难,因为内核提供的用于处理大量I/O(epoll等)的工具并没有充分利用起来。更糟糕的是,为每个请求运行一个单独的进程往往会占用大量的系统资源,尤其是内存,这通常是第一个耗尽的。&/p&&p&*注意:在这一点上,Ruby的情况与PHP非常相似。&/p&&p&多线程:Java&/p&&p&所以,Java就出现了。而且Java在语言中内置了多线程,特别是在创建线程时非常得棒。&/p&&p&大多数的Java Web服务器都会为每个请求启动一个新的执行线程,然后在这个线程中调用开发人员编写的函数。&/p&&p&在Java Servlet中执行I/O往往是这样的:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&publicvoiddoGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException
// blocking file I/O
InputStream fileIs = new FileInputStream(&/path/to/file&);
// blocking network I/O
URLConnection urlConnection = (new URL(&http://example.com/example-microservice&)).openConnection();
InputStream netIs = urlConnection.getInputStream();
// some more blocking network I/O
out.println(&...&);
&/code&&/pre&&/div&&p&由于上面的 &code&doGet&/code& 方法对应于一个请求,并且在自己的线程中运行,而不是在需要有独立内存的单独进程中运行,所以我们将创建一个单独的线程。每个请求都会得到一个新的线程,并在该线程内部阻塞各种I/O操作,直到请求处理完成。应用会创建一个线程池以最小化创建和销毁线程的成本,但是,成千上万的连接意味着有成千上万的线程,这对于调度器来说并不件好事情。&/p&&p&值得注意的是,1.4版本的Java(1.7版本中又重新做了升级)增加了非阻塞I/O调用的能力。虽然大多数的应用程序都没有使用这个特性,但它至少是可用的。一些Java Web服务器正在尝试使用这个特性,但绝大部分已经部署的Java应用程序仍然按照上面所述的原理进行工作。&/p&&figure&&img src=&https://pic3.zhimg.com/v2-9ad6947b9bda32b4baae6ecf9f1ea7a4_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&550& data-rawheight=&550& class=&origin_image zh-lightbox-thumb& width=&550& data-original=&https://pic3.zhimg.com/v2-9ad6947b9bda32b4baae6ecf9f1ea7a4_r.jpg&&&/figure&&p&Java提供了很多在I/O方面开箱即用的功能,但如果遇到创建大量阻塞线程执行大量I/O操作的情况时,Java也没有太好的解决方案。&/p&&p&把非阻塞I/O作为头等大事:Node&/p&&p&在I/O方面表现比较好的、比较受用户欢迎的是Node.js。任何一个对Node有简单了解的人都知道,它是“非阻塞”的,并且能够高效地处理I/O。这在一般意义上是正确的。但是细节和实现的方式至关重要。&/p&&p&在需要做一些涉及I/O的操作的时候,你需要发出请求,并给出一个回调函数,Node会在处理完请求之后调用这个函数。&/p&&p&在请求中执行I/O操作的典型代码如下所示:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&http.createServer(function(request, response) {
fs.readFile('/path/to/file', 'utf8', function(err, data) {
response.end(data);
&/code&&/pre&&/div&&p&如上所示,这里有两个回调函数。当请求开始时,第一个函数会被调用,而第二个函数是在文件数据可用时被调用。&/p&&p&这样,Node就能更有效地处理这些回调函数的I/O。有一个更能说明问题的例子:在Node中调用数据库操作。首先,你的程序开始调用数据库操作,并给Node一个回调函数,Node会使用非阻塞调用来单独执行I/O操作,然后在请求的数据可用时调用你的回调函数。这种对I/O调用进行排队并让Node处理I/O调用然后得到一个回调的机制称为“事件循环”。这个机制非常不错。&/p&&figure&&img src=&https://pic2.zhimg.com/v2-cfb334c8b36f39fa0953f4_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&550& data-rawheight=&550& class=&origin_image zh-lightbox-thumb& width=&550& data-original=&https://pic2.zhimg.com/v2-cfb334c8b36f39fa0953f4_r.jpg&&&/figure&&p&然而,这个模型有一个问题。在底层,这个问题出现的原因跟V8 JavaScript引擎(Node使用的是Chrome的JS引擎)的实现有关,即:你写的JS代码都运行在一个线程中。请思考一下。这意味着,尽管使用高效的非阻塞技术来执行I/O,但是JS代码在单个线程操作中运行基于CPU的操作,每个代码块都会阻塞下一个代码块的运行。有一个常见的例子:在数据库记录上循环,以某种方式处理记录,然后将它们输出到客户端。下面这段代码展示了这个例子的原理:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&var handler = function(request, response) {
connection.query('SELECT ...', function(err, rows) {if (err) { throw err };
for (var i = 0; i & rows. i++) {
// do processing on each row
response.end(...); // write out the results
&/code&&/pre&&/div&&p&虽然Node处理I/O的效率很高,但是上面例子中的 &code&for&/code& 循环在一个主线程中使用了CPU周期。这意味着如果你有10000个连接,那么这个循环就可能会占用整个应用程序的时间。每个请求都必须要在主线程中占用一小段时间。&/p&&p&这整个概念的前提是I/O操作是最慢的部分,因此,即使串行处理是不得已的,但对它们进行有效处理也是非常重要的。这在某些情况下是成立的,但并非一成不变。&/p&&p&另一点观点是,写一堆嵌套的回调很麻烦,有些人认为这样的代码很丑陋。在Node代码中嵌入四个、五个甚至更多层的回调并不罕见。&/p&&p&又到了权衡利弊的时候了。如果你的主要性能问题是I/O的话,那么这个Node模型能帮到你。但是,它的缺点在于,如果你在一个处理HTTP请求的函数中放入了CPU处理密集型代码的话,一不小心就会让每个连接都出现拥堵。&/p&&p&原生无阻塞:Go&/p&&p&在介绍Go之前,我透露一下,我是一个Go的粉丝。我已经在许多项目中使用了Go。&/p&&p&让我们看看它是如何处理I/O的吧。 Go语言的一个关键特性是它包含了自己的调度器。它并不会为每个执行线程对应一个操作系统线程,而是使用了“goroutines”这个概念。Go运行时会为一个goroutine分配一个操作系统线程,并控制它执行或暂停。Go HTTP服务器的每个请求都在一个单独的Goroutine中进行处理。&/p&&p&调度程序的工作原理如下所示:&/p&&figure&&img src=&https://pic1.zhimg.com/v2-c82c2dca99aec5ed8d70a_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&550& data-rawheight=&550& class=&origin_image zh-lightbox-thumb& width=&550& data-original=&https://pic1.zhimg.com/v2-c82c2dca99aec5ed8d70a_r.jpg&&&/figure&&p&实际上,除了回调机制被内置到I/O调用的实现中并自动与调度器交互之外,Go运行时正在做的事情与Node不同。它也不会受到必须让所有的处理代码在同一个线程中运行的限制,Go会根据其调度程序中的逻辑自动将你的Goroutine映射到它认为合适的操作系统线程中。因此,它的代码是这样的:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&func ServeHTTP(w http.ResponseWriter, r *http.Request) {
// the underlying network call here is non-blocking
rows, err := db.Query(&SELECT ...&)
for _, row := range rows {
// do something with the rows,// each request in its own goroutine
w.Write(...) // write the response, also non-blocking
&/code&&/pre&&/div&&p&如上所示,这样的基本代码结构更为简单,而且还实现了非阻塞I/O。&/p&&p&在大多数情况下,这真正做到了“两全其美”。非阻塞I/O可用于所有重要的事情,但是代码却看起来像是阻塞的,因此这样往往更容易理解和维护。 剩下的就是Go调度程序和OS调度程序之间的交互处理了。这并不是魔法,如果你正在建立一个大型系统,那么还是值得花时间去了解它的工作原理的。同时,“开箱即用”的特点使它能够更好地工作和扩展。&/p&&p&Go可能也有不少缺点,但总的来说,它处理I/O的方式并没有明显的缺点。&/p&&p&性能评测&/p&&p&对于这些不同模型的上下文切换,很难进行准确的计时。当然,我也可以说这对你并没有多大的用处。这里,我将对这些服务器环境下的HTTP服务进行基本的性能评测比较。请记住,端到端的HTTP请求/响应性能涉及到的因素有很多。&/p&&p&我针对每一个环境都写了一段代码来读取64k文件中的随机字节,然后对其运行N次SHA-256散列(在URL的查询字符串中指定N,例如 &code&.../test.php?n=100&/code& )并以十六进制打印结果。我之所以选择这个,是因为它可以很容易运行一些持续的I/O操作,并且可以通过受控的方式来增加CPU使用率。&/p&&p&首先,我们来看一些低并发性的例子。使用300个并发请求运行2000次迭代,每个请求哈希一次(N=1),结果如下:&/p&&figure&&img src=&https://pic3.zhimg.com/v2-905f96cedfbb196dc1cb5cc_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&550& data-rawheight=&550& class=&origin_image zh-lightbox-thumb& width=&550& data-original=&https://pic3.zhimg.com/v2-905f96cedfbb196dc1cb5cc_r.jpg&&&/figure&&p&Times是完成所有并发请求的平均毫秒数。越低越好。&/p&&p&从单单这一张图中很难得到结论,但我个人认为,在这种存在大量连接和计算的情况下,我们看到的结果更多的是与语言本身的执行有关。请注意,“脚本语言”的执行速度最慢。&/p&&p&但是如果我们将N增加到1000,但仍然是300个并发请求,即在相同的负载的情况下将散列的迭代次数增加了1000倍(CPU负载明显更高),会发生什么情况呢:&/p&&figure&&img src=&https://pic3.zhimg.com/v2-d6b5e97bc91b931da30e_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&550& data-rawheight=&550& class=&origin_image zh-lightbox-thumb& width=&550& data-original=&https://pic3.zhimg.com/v2-d6b5e97bc91b931da30e_r.jpg&&&/figure&&p&Times是完成所有并发请求的平均毫秒数。越低越好。&/p&&p&突然之间,由于每个请求中的CPU密集型操作相互阻塞,Node的性能显著下降。有趣的是,在这个测试中,PHP的性能变得更好了(相对于其他),甚至优于Java。 (值得注意的是,在PHP中,SHA-256的实现是用C语言编写的,但执行路径在这个循环中花费了更多的时间,因为我们这次做了1000次哈希迭代)。&/p&&p&现在,让我们试试5000个并发连接(N=1) 。不幸的是,对于大多数的环境来说,失败率并不明显。我们来看看这个图表中每秒处理的请求数, &i&越高越好&/i& :&/p&&figure&&img src=&https://pic3.zhimg.com/v2-d68f066ab3e80f3aa2ca6d_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&550& data-rawheight=&550& class=&origin_image zh-lightbox-thumb& width=&550& data-original=&https://pic3.zhimg.com/v2-d68f066ab3e80f3aa2ca6d_r.jpg&&&/figure&&p&每秒处理的请求数,越高越好。&/p&&p&这个图看起来跟上面的不太一样。我猜测,在较高的连接数量下,PHP + Apache中产生新进程和内存的申请似乎成为了影响PHP性能的主要因素。 很显然,Go是这次的赢家,其次是Java,Node,最后是PHP。&/p&&p&虽然涉及到整体吞吐量的因素很多,而且应用程序和应用程序之间也存在着很大的差异,但是,越是了解底层的原理和所涉及的权衡问题,应用程序的表现就会越好。&/p&&p&总结&/p&&p&综上所述,随着语言的发展,处理大量I/O大型应用程序的解决方案也随之发展。&/p&&p&公平地说,PHP和Java在 &b&&i&&a href=&https://link.zhihu.com/?target=https%3A//netty.io/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&web应用方面&/a&&/i&&/b& 都有 &b&&i&&a href=&https://link.zhihu.com/?target=http%3A//undertow.io/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&可用&/a&&/i&&/b& 的 &b&&i&&a href=&https://link.zhihu.com/?target=http%3A//amphp.org/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&非阻塞I/O&/a&&/i&&/b& 的 &b&&i&&a href=&https://link.zhihu.com/?target=http%3A//reactphp.org/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&实现&/a&&/i&&/b& 。但是这些实现并不像上面描述的方法那么使用广泛,并且还需要考虑维护上的开销。更不用说应用程序的代码必须以适合这种环境的方式来构建。&/p&&p&我们来比较一下几个影响性能和易用性的重要因素:&/p&&p&语言 线程与进程 非阻塞I/O 易于使用PHP进程否-Java线程有效需要回调Node.js线程是需要回调Go线程 (Goroutines)是无需回调&/p&&p&因为线程会共享相同的内存空间,而进程不会,所以线程通常要比进程的内存效率高得多。在上面的列表中,从上往下看,与I/O相关的因素一个比一个好。所以,如果我不得不在上面的比较中选择一个赢家,那肯定选Go。&/p&&p&即便如此,在实践中,选择构建应用程序的环境与你团队对环境的熟悉程度以及团队可以实现的整体生产力密切相关。所以,对于团队来说,使用Node或Go来开发Web应用程序和服务可能并不是最好的选择。&/p&&p&希望以上这些内容能够帮助你更清楚地了解底层发生的事情,并为你提供一些关于如何处理应用程序伸缩性的建议。&/p&
原文: 作者:BRAD PEABODY翻译:雁惊寒摘要:本文首先简单介绍了I/O相关的基础概念,然后横向比较了Node、PHP、Java、Go的I/O性能,并给出了选型建议。以下是译文。了解应用程序的输入/输出(I/…
&p&再来补充一个比较好用的浏览器插件——&b&&a href=&//link.zhihu.com/?target=http%3A//www.bumblebeesystems.com/wastenotime/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&wastenotime&/a&&/b&,能帮助各位没有自控力且注意力不集中的小伙伴们,相信我,只要你不皮,这个软件的效果真的是立竿见影!&/p&&a class=&video-box& href=&//link.zhihu.com/?target=https%3A//www.zhihu.com/video/962944& target=&_blank& data-video-id=&& data-video-playable=&true& data-name=&& data-poster=&https://pic2.zhimg.com/80/v2-689d87b881b5f9bdeaf483f_b.jpg& data-lens-id=&962944&&
&img class=&thumbnail& src=&https://pic2.zhimg.com/80/v2-689d87b881b5f9bdeaf483f_b.jpg&&&span class=&content&&
&span class=&title&&&span class=&z-ico-extern-gray&&&/span&&span class=&z-ico-extern-blue&&&/span&&/span&
&span class=&url&&&span class=&z-ico-video&&&/span&https://www.zhihu.com/video/962944&/span&
&/a&&p&&br&&/p&&p&当你正在全神贯注地学习、策划或者写作,不得不通过网站搜索查找一些可能需要的内容,结果过了三小时,你发现自己正在刷微博、看头条,甚至追网剧……你不明白这是怎么回事,但一定是哪里发生了什么。 &/p&&p&&br&&/p&&p&不要把这些结果完全归咎于自己的低效率,事实上,在现代社会中,十个人有九个,都因为科技和信息的干扰,拥有所谓的注意力缺乏症。但我们同样可以因为科技所产生的工具,让一切变得更高效。比如这款名为 wastenotime 的高效率插件。&/p&&p&&br&&/p&&p&在安装有 wastenotime 的浏览器当中,点击这里的时间按钮,将自动跳转出插件界面,首先根据自己的工作习惯点击这里的“设置”进行基本设置。比如在“黑名单”中,将我需要控制时间的网站添加进去,像微博、头条、知乎、非死不可等等。在“时间允许配额”中,确定自己的工作时间为早上9点到下午6点,限定我在工作中可以访问黑名单上的网站时间为30分钟甚至更少,工作外的为120分钟甚至更少;&/p&&p&&br&&/p&&p&这样,超过我既定的时间,这些不应该被访问的网站就直接无法访问。如果你害怕自己手欠会去更改wastenotime的设置,没关系,这里有一个冲动抑制器。比如设置输入一串随机的坑爹字符,如果你真的管不住自己双手的话,就慢慢输入吧。&/p&&p&&br&&/p&&p&在时间追踪选项中,你可以看到自己每天、每周、每月在不同网站上所花费的时间,你就知道,自己的时间去了哪里。&/p&&p&&br&&/p&&p&wastenotime 目前支持 Chrome 和 Safari 浏览器,还有一款类似的名为 leechblock 的软件,支持 Firefox 浏览器。你可以直接通过官网下载,当然也可以在我们的微信后台回复 wastenotime 获取下载链接,注意字母之间没有空格。&/p&&p&&/p&
再来补充一个比较好用的浏览器插件——,能帮助各位没有自控力且注意力不集中的小伙伴们,相信我,只要你不皮,这个软件的效果真的是立竿见影! 当你正在全神贯注地学习、策划或者写作,不得不通过网站搜索查找一些可能需要的内容,结果过了三小…
&figure&&img src=&https://pic2.zhimg.com/v2-9f6f1df47b2e3d50ccb44e7fd1d278fd_b.jpg& data-rawwidth=&1000& data-rawheight=&553& class=&origin_image zh-lightbox-thumb& width=&1000& data-original=&https://pic2.zhimg.com/v2-9f6f1df47b2e3d50ccb44e7fd1d278fd_r.jpg&&&/figure&&p&在&a href=&http://link.zhihu.com/?target=https%3A//www.youtube.com/playlist%3Flist%3DPLIivdWyY5sqJxnwJhe3etaK7utrBiPBQ2& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&这些视频&/a&中,你已经看到过我在屏幕上直播写的 Python 代码以及这些代码的运行结果了。那么今天我就来跟大家分享一下我是如何实现,以及一些相关的实用技巧。&/p&&blockquote&这是一个系列视频/文章 「AI Adventures」中的第十四篇,由 Google 的开发技术推广工程师 Yufeng Guo 主讲,用通俗易懂的语言帮助你了解人工智能和机器学习。在这一系列视频/文章中,我们将一起探秘人工智能的世界,共同品味艺术、探索科学以及掌握机器学习的工具。&br&第一篇:&a href=&https://zhuanlan.zhihu.com/p/& class=&internal&&机器学习是什么?&/a&&br&第二篇:&a href=&https://zhuanlan.zhihu.com/p/& class=&internal&&机器学习「七步走」&/a&&br&第三篇:&a href=&https://zhuanlan.zhihu.com/p/& class=&internal&&用评估器给花卉分类&/a&&br&第四篇:&a href=&https://zhuanlan.zhihu.com/p/& class=&internal&&弹性伸缩的云端托管服务&/a&&br&第五篇:&a href=&https://zhuanlan.zhihu.com/p/& class=&internal&&通过 TensorBoard 将模型可视化&/a&&br&第六篇:&a href=&https://zhuanlan.zhihu.com/p/& class=&internal&&通过深度神经网络再识评估器&/a&&br&第七篇:&a href=&https://zhuanlan.zhihu.com/p/& class=&internal&&云端训练模型的大数据解决方案&/a&&br&第八篇:&a href=&https://zhuanlan.zhihu.com/p/& class=&internal&&跟着 Google Research 体验自然语言生成&/a&&br&第九篇:&a href=&https://zhuanlan.zhihu.com/p/& class=&internal&&云上的机器学习引擎&/a&&br&第十篇:&a href=&https://zhuanlan.zhihu.com/p/& class=&internal&&使用 MNIST 数据集训练模型&/a&&br&第十一篇:&a href=&https://zhuanlan.zhihu.com/p/& class=&internal&&机器学习工程师用 Python 开发环境的最佳实践&/a&&br&第十二篇:&a href=&https://zhuanlan.zhihu.com/p/& class=&internal&&机器学习之前,让「大熊猫」先尝一尝数据的味道&/a&&br&第十三篇:&a href=&https://zhuanlan.zhihu.com/p/& class=&internal&&机器学习大杀器:爆米花般火爆的 Kaggle Kernels&/a&&br&所有的内容和视频都会首发在「&a href=&https://zhuanlan.zhihu.com/tensorflowcn& class=&internal&&机智如你&/a&」专栏,目标是发布最新与谷歌相关的机器学习、TensorFlow 相关内容,如果你有任何问题,也欢迎在留言区向我们提出反馈。&/blockquote&&a class=&video-box& href=&http://link.zhihu.com/?target=https%3A//www.zhihu.com/video/036608& target=&_blank& data-video-id=&& data-video-playable=&true& data-name=&& data-poster=&https://pic7.zhimg.com/v2-aff256ee402ca5.jpg& data-lens-id=&036608&&
&img class=&thumbnail& src=&https://pic7.zhimg.com/v2-aff256ee402ca5.jpg&&&span class=&content&&
&span class=&title&&&span class=&z-ico-extern-gray&&&/span&&span class=&z-ico-extern-blue&&&/span&&/span&
&span class=&url&&&span class=&z-ico-video&&&/span&https://www.zhihu.com/video/036608&/span&
&p&我在屏幕上直接运行 Python 代码是通过一个叫 &a href=&http://link.zhihu.com/?target=http%3A//jupyter.org/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Jupyter&/a& 的 Python 包来实现的。Jupyter 是基于 IPython 项目开发的,它让开发者能够在浏览器中感受交互式的 Python 编程体验。但是它能做的可远不止这些,无论是 bash 命令、Magic 魔法操作符还是一些插件,Jupyter 都极大改善了你的 Python 编程体验。&/p&&p&如果你已经是 Jupyter 的老手了,希望我这些分享能改善你的工作流程且提供一些技巧贴士。加入你还没用过,也不需要担心,我们一起探讨吧。&/p&&p&「译者注」文末对 Jupyter 和 IPython 有一定的补充,广大读者可以参阅。&/p&&hr&&h2&安装和启动&/h2&&p&安装 Jupyter 最简单的方式是执行 &code&pip install jupyter&/code& 命令(当然,如果你用了一些自带这些包的 Python 发行版,忽略这个最简单)。一定要首先搞定 Python 环境,我们一起来操作一下。&/p&&p&当在本地运行 Jupyter 时,你将通过浏览器访问运行在本机的一个 Web 服务器,常常是在 8888 端口上的。在当前的工作目录执行 &code&jupyter notebook&/code& 就能运行起来了。通常 Jupyter 会在启动后自动打开浏览器访问 Web 页面,不过假如你的没有这样,则可以在浏览器手动输入 &code&localhost:8888&/code& 访问。&/p&&figure&&img src=&https://pic4.zhimg.com/v2-8c68fcf38acd57ff9b127f0b_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&800& data-rawheight=&500& data-thumbnail=&https://pic2.zhimg.com/v2-8c68fcf38acd57ff9b127f0b_b.jpg& class=&origin_image zh-lightbox-thumb& width=&800& data-original=&https://pic2.zhimg.com/v2-8c68fcf38acd57ff9b127f0b_r.jpg&&&/figure&&p&如果环境中没有你需要打开的 notebook,那么请通过点击「New」并选择对应的版本(取决于你环境变量中的 Python 版本)。Jupyter 伸缩性极强,能够提供多种语言和文件格式支持,而目前我们只用得上 Python。&/p&&h2&在 Jupyter Notebooks 中执行代码&/h2&&p&开启新的 notebook 之后,我们就可以在空格当中写一些 Python 代码,然后按下 &code&ctrl+enter&/code& 来执行。我们可以在其中编写和运行通常那些在 Python 脚本中使用的代码。其差别就是我们可以立即执行并看到结果。&/p&&p&注意观察当我们按下 &code&ctrl+enter&/code& 的时候,空格左侧的方括号会出现一个星号,当执行结束之后星号会编程表示当前执行的操作在整个会话中的次序(从 1 开始)。&/p&&a class=&video-box& href=&http://link.zhihu.com/?target=https%3A//www.zhihu.com/video/563584& target=&_blank& data-video-id=&& data-video-playable=&true& data-name=&& data-poster=&https://pic3.zhimg.com/80/v2-33a848f444d265a357fd4a_b.jpg& data-lens-id=&563584&&
&img class=&thumbnail& src=&https://pic3.zhimg.com/80/v2-33a848f444d265a357fd4a_b.jpg&&&span class=&content&&
&span class=&title&&&span class=&z-ico-extern-gray&&&/span&&span class=&z-ico-extern-blue&&&/span&&/span&
&span class=&url&&&span class=&z-ico-video&&&/span&https://www.zhihu.com/video/563584&/span&
&p&最后一行命令的执行结果会被输出到界面中作为此格操作的输出信息,但这个特性在当你的最后一句是将某个值存于变量中时不会生效。举个例子,我将 tensorflow 包引入,并将其版本与一个字符串连接,最终输出如下(注意我没有使用 print 命令)。&/p&&figure&&img src=&https://pic2.zhimg.com/v2-e20c0c5ca5b81e051fb2f5_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1000& data-rawheight=&625& class=&origin_image zh-lightbox-thumb& width=&1000& data-original=&https://pic2.zhimg.com/v2-e20c0c5ca5b81e051fb2f5_r.jpg&&&/figure&&p&当然,我也能用 print() 函数来实现。不过这一特性在摆弄代码、观察结果方面十分有用。&/p&&h2&Shift-Tab 组合键&/h2&&p&Jupyter notebook 的另一个非常赞的特性是:你可以通过按下 &code&shift-tab&/code& 组合键来显示当前函数的说明摘要。这使得你可以很轻松而准确地进行函数调用,而不需要每次都在完整说明文档中苦苦寻找。&/p&&a class=&video-box& href=&http://link.zhihu.com/?target=https%3A//www.zhihu.com/video/845696& target=&_blank& data-video-id=&& data-video-playable=&true& data-name=&& data-poster=&https://pic2.zhimg.com/80/v2-536fbd4c2afe26adb6fff31_b.jpg& data-lens-id=&845696&&
&img class=&thumbnail& src=&https://pic2.zhimg.com/80/v2-536fbd4c2afe26adb6fff31_b.jpg&&&span class=&content&&
&span class=&title&&&span class=&z-ico-extern-gray&&&/span&&span class=&z-ico-extern-blue&&&/span&&/span&
&span class=&url&&&span class=&z-ico-video&&&/span&https://www.zhihu.com/video/845696&/span&
&p&此特性对于你的自定义函数仍然有效。所以说文档写得好,麻烦也减少!&/p&&h2&输出的内容&/h2&&p&当你输出了大量的内容时,你可以通过单击输出区域左侧的面板来讲它们收拢,这样就可以通过滚动来查看了。如果双击,则会直接将所有的输出都收起来,不再展示。&/p&&a class=&video-box& href=&http://link.zhihu.com/?target=https%3A//www.zhihu.com/video/510784& target=&_blank& data-video-id=&& data-video-playable=&true& data-name=&& data-poster=&https://pic3.zhimg.com/80/v2-8ab6f98ecdb22_b.jpg& data-lens-id=&510784&&
&img class=&thumbnail& src=&https://pic3.zhimg.com/80/v2-8ab6f98ecdb22_b.jpg&&&span class=&content&&
&span class=&title&&&span class=&z-ico-extern-gray&&&/span&&span class=&z-ico-extern-blue&&&/span&&/span&
&span class=&url&&&span class=&z-ico-video&&&/span&https://www.zhihu.com/video/510784&/span&
&h2&格子++&/h2&&p&一个空格其实已经很棒了,但我们的确需要更多的格子。此时,点击工具栏的加号就能创建一个新的格子,而其他的还有一些命令可以创建新的格子。&/p&&figure&&img src=&https://pic2.zhimg.com/v2-3fc151b0b5c6f6fde4ef15d_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1000& data-rawheight=&625& data-thumbnail=&https://pic2.zhimg.com/v2-3fc151b0b5c6f6fde4ef15d_b.jpg& class=&origin_image zh-lightbox-thumb& width=&1000& data-original=&https://pic2.zhimg.com/v2-3fc151b0b5c6f6fde4ef15d_r.jpg&&&/figure&&p&如果你点击 &code&shift+enter&/code&,当前格子当中的内容会被执行,然后后面的格子会高亮显示。如果后面没有新的格子了,那么 Jupyter 会帮你创建一个新格子。另一方面,如果你想在给定的格子后面立即创建新的一格,则可以使用 &code&alt-enter&/code& 组合键执行当前格子的内容,然后在后面插入一个新的格子。&/p&&a class=&video-box& href=&http://link.zhihu.com/?target=https%3A//www.zhihu.com/video/730048& target=&_blank& data-video-id=&& data-video-playable=&true& data-name=&& data-poster=&https://pic1.zhimg.com/80/v2-ecc3be2e03f65dedc5c4_b.jpg& data-lens-id=&730048&&
&img class=&thumbnail& src=&https://pic1.zhimg.com/80/v2-ecc3be2e03f65dedc5c4_b.jpg&&&span class=&content&&
&span class=&title&&&span class=&z-ico-extern-gray&&&/span&&span class=&z-ico-extern-blue&&&/span&&/span&
&span class=&url&&&span class=&z-ico-video&&&/span&https://www.zhihu.com/video/730048&/span&
&h2&诶,你要用 Markdown?&/h2&&p&可能目前我没提到的最赞的特性就是 Markdown 语法支持了。我对 Jupyter notebooks 的第一印象是它能够同时写代码和描述代码的特性。Markdown 的富语义特性使之为研究者、讲师等角色能够轻松简洁交流、传播其观点。&/p&&figure&&img src=&https://pic2.zhimg.com/v2-16beffbeba780416adcca_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1000& data-rawheight=&625& data-thumbnail=&https://pic3.zhimg.com/v2-16beffbeba780416adcca_b.jpg& class=&origin_image zh-lightbox-thumb& width=&1000& data-original=&https://pic3.zhimg.com/v2-16beffbeba780416adcca_r.jpg&&&/figure&&p&并且最重要的是,它让「曾经的你」向「将来的你」讲解某些代码具体是做什么的,这比起用评论块的方式不知道高到哪里去了。&/p&&h2&Jupyter Magics 魔法操作符&/h2&&p&有时候,我只想快速查看某个训练或评估流程占据了多少时间。一种简便的方式是在空格的一开始使用 %%time 魔法操作符,一旦当前格子执行完成,它将会输出执行的耗时。虽说它不像原子时那么精准,但却是一种花最小力气就能了解到大致情况的好办法。&/p&&figure&&img src=&https://pic1.zhimg.com/v2-e57f86e96fb798aab811e59df5afc568_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1000& data-rawheight=&625& class=&origin_image zh-lightbox-thumb& width=&1000& data-original=&https://pic1.zhimg.com/v2-e57f86e96fb798aab811e59df5afc568_r.jpg&&&/figure&&p&如果你想要在 notebook 中执行命令行语句,最简单的方式是在语句最前面加上一个感叹号。这种便利在执行一次性语句时尤为明显。&/p&&figure&&img src=&https://pic1.zhimg.com/v2-34c2a984e02b4cbd370410_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1000& data-rawheight=&222& class=&origin_image zh-lightbox-thumb& width=&1000& data-original=&https://pic1.zhimg.com/v2-34c2a984e02b4cbd370410_r.jpg&&&/figure&&p&假如你要执行的是一段命令,那么在格子最前面使用 %%bash 魔法操作符就能将整个格子的内容解释成 bash 脚本了。&/p&&figure&&img src=&https://pic4.zhimg.com/v2-a246f1bf21d223ce06caed_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1000& data-rawheight=&625& class=&origin_image zh-lightbox-thumb& width=&1000& data-original=&https://pic4.zhimg.com/v2-a246f1bf21d223ce06caed_r.jpg&&&/figure&&p&这一特性的典型应用场景是 &a href=&http://link.zhihu.com/?target=https%3A//towardsdatascience.com/visualizing-your-model-using-tensorboard-796ebb73e98d& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&TensorBoard&/a&。通常来说,启动 TensorBoard 需要开启新的命令行窗口,然后在其中执行相关的指令。但是如果你仅仅开启了看一下就要关掉,则可以放心在 Jupyter notebooks 的 bash 格子当中执行。&/p&&p&另外,你肯定不会忘记执行这个命令,因为它就存在于你的 Jupyter notebooks 当中。注意,这种方式会使得 TensorBoard 进程占据 Jupyter,也就是你此时不能再运行别的命令。要想停下该进程,只需点击工具栏上的 interrupt 按钮即可,这样你又回到了正常的操作模式中。&/p&&a class=&video-box& href=&http://link.zhihu.com/?target=https%3A//www.zhihu.com/video/665920& target=&_blank& data-video-id=&& data-video-playable=&true& data-name=&& data-poster=&https://pic3.zhimg.com/80/v2-a27a65d4f31ad0e1bab40a_b.jpg& data-lens-id=&665920&&
&img class=&thumbnail& src=&https://pic3.zhimg.com/80/v2-a27a65d4f31ad0e1bab40a_b.jpg&&&span class=&content&&
&span class=&title&&&span class=&z-ico-extern-gray&&&/span&&span class=&z-ico-extern-blue&&&/span&&/span&
&span class=&url&&&span class=&z-ico-video&&&/span&https://www.zhihu.com/video/665920&/span&
&p&好啦,这些就是我特别喜欢的 Jupyter 特性啦。当然,这些并不是对其功能的全面描述、讲解,我只是提供了一些我最常用的而已。还有很多很多的内容,值得你到&a href=&http://link.zhihu.com/?target=http%3A//jupyter.org/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&官网&/a&去探索。&/p&&hr&&p&感谢阅读本期 &a href=&http://link.zhihu.com/?target=https%3A//goo.gl/UC5usG& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Cloud AI Adventures&/a& 分享。如果你喜欢这个系列,请点个赞。如果你想学到更多关于机器学习的知识,请关注我或者&a href=&http://link.zhihu.com/?target=https%3A//medium.com/%40yufengg& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&& Yufeng G 的 Medium&/a& 亦或是关注《&a href=&https://zhuanlan.zhihu.com/tensorflowcn& class=&internal&&机智如你&/a&》专栏或是 &a href=&http://link.zhihu.com/?target=https%3A//goo.gl/S0AS51& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&YouTube 频道&/a&。赶紧关注,不要错过更多精彩!&/p&&p&P.S. 你觉得文中的 gif 怎么样?它们是否有帮助?或者影响了你阅读的节奏?它们简明扼要了还是让你更加疑惑了?请告诉我!&/p&&hr&&p&▏原文出处:&a href=&http://link.zhihu.com/?target=https%3A//towardsdatascience.com/interactive-data-science-with-jupyter-notebooks-457ab4928b08& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Medium - Interactive Data Science with Jupyter Notebooks&/a&&/p&&p&▏封面来源:&a href=&http://link.zhihu.com/?target=https%3A//youtu.be/2eCHD6f_phE%3Flist%3DPLIivdWyY5sqJxnwJhe3etaK7utrBiPBQ2& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&YouTube 视频缩略图&/a&&/p&&p&▏视频出处:&a href=&http://link.zhihu.com/?target=https%3A//youtu.be/2eCHD6f_phE%3Flist%3DPLIivdWyY5sqJxnwJhe3etaK7utrBiPBQ2& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&YouTube - Jupyter Tips and Tricks&/a&&/p&&p&▏字幕翻译:&a href=&https://zhuanlan.zhihu.com/gdsub& class=&internal&&谷创字幕组&/a&&/p&&p&▏文章编辑: &a class=&member_mention& href=&http://www.zhihu.com/people/bdf& data-hash=&bdf& data-hovercard=&p$b$bdf&&@杨栋&/a&&/p&&hr&&p&「尾注1」Yufeng 的原文中使用了很多精美的 gif 图像,但是由于知乎的限制,无法将 gif 上传,必须采用替代办法。此前,我们两次将 gif 上传到了 CDN 中,在原文中提供 gif 中某一帧截图,并后附 CDN 中 gif 的链接地址。本期文章我们尝试将 gif 转为了小段的视频,除了减少了流量消耗,还希望能够给大家更简便、快捷的阅读体验。请将你宝贵的反馈信息反馈给我们,以便我们今后使用最佳方案创作,谢谢。&/p&&p&「尾注2」Jupyter 的特性在文中已经介绍了不少了,这里进行一个简单概括、补充。Jupyter 本身是基于 IPython 项目开发的,所以继承了来自 IPython 的大量特性。当你使用 Jupyter notebooks 功能时就像是在使用一款集成了开发、文档、调试于一体的 Machine Learning IDE。文中的格子是 Jupyter 的最小可执行单位,每一段代码都会在其中执行。&/p&
在中,你已经看到过我在屏幕上直播写的 Python 代码以及这些代码的运行结果了。那么今天我就来跟大家分享一下我是如何实现,以及一些相关的实用技巧。这是一个系列视频/文章 「AI Adventures」中的第十四篇,由 Google 的开发技术推广工程师 Yufeng…
&figure&&img src=&https://pic2.zhimg.com/v2-e60cbdd1fc5b8b_b.jpg& data-rawwidth=&900& data-rawheight=&500& class=&origin_image zh-lightbox-thumb& width=&900& data-original=&https://pic2.zhimg.com/v2-e60cbdd1fc5b8b_r.jpg&&&/figure&&p&啊~,终于整理了这个世上最好语言的PHP学习资源,希望对在学习PHP的小伙伴有所帮助~&/p&&p&我大概会&b&从以下五个方面整理PHP资源&/b&,其中前两个方面适合新手小白查看,如果你是非小白学习者可以直接查看后面三个,里面可以找到适合自己的资源;&br&&/p&&ul&&li&1)&b&PHP学习路径&/b&:可以让新手学习者把握PHP学习方向,不迷茫;&/li&&li&2)&b&在线教程&/b&:分享一些不错的PHP学习教程以及网站;&br&&/li&&li&3)&b&文档及博客&/b&:PHP手册及博文网站;&br&&/li&&li&4)&b&资源整理&/b&:整理一些PHP资源,可以找到超多资源;&br&&/li&&li&5)&b&笔记整理&/b&:PHP学习笔记的集合;&br&&/li&&/ul&&br&&p&&b&一、PHP学习路径:&/b&&/p&&p&因为太多新手在学习的时候,找不到方向,不知道接下来该学什么,所以&PHP学习路径&就非常适合想全面了解PHP技术体系或者想知道该学习什么的小伙伴,看完后可以对整个学习方向有个大概的认知和把握,使得学习不迷茫。因此非常推荐所有PHP学习者都看看。&/p&&ul&&li&&b&&a href=&http://link.zhihu.com/?target=http%3A//lib.csdn.net/base/php/structure& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&PHP知识图谱&/a&——CSDN&/b&&br&&/li&&/ul&&br&&figure&&img src=&https://pic3.zhimg.com/v2-41f44c1d008f8e146f5c2_b.jpg& data-rawwidth=&980& data-rawheight=&978& class=&origin_image zh-lightbox-thumb& width=&980& data-original=&https://pic3.zhimg.com/v2-41f44c1d008f8e146f5c2_r.jpg&&&/figure&&ul&&li&&b&&a href=&http://link.zhihu.com/?target=https%3A//www.shiyanlou.com/paths/php& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&PHP研发工程师&/a&——实验楼&/b&&br&&/li&&/ul&&br&&figure&&img src=&https://pic1.zhimg.com/v2-501b02e5096ecc48deae14_b.png& data-rawwidth=&913& data-rawheight=&4033& class=&origin_image zh-lightbox-thumb& width=&913& data-original=&https://pic1.zhimg.com/v2-501b02e5096ecc48deae14_r.jpg&&&/figure&&ul&&li&&b&&a href=&http://link.zhihu.com/?target=http%3A//www.imooc.com/course/programdetail/pid/34& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&}

我要回帖

更多关于 www.335yu.com 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信