Category: Programming

  • RxJava与常见控制结构

    在编程语言里面,控制结构是比较基础的一块内容。常见的是顺序,分支和循环。部分语言有try catch finally结构等。在使用RxJava时,如何对应既有的控制结构,或者说既有代码如何转换成RxJava的代码,是学习RxJava时必须熟练掌握的东西。 顺序结构 在三种常见控制结构里面,顺序其实是比较难的一种结构。比如说以下两行顺序代码 [crayon-64256f5bc6df6383243130/] 和另外一类顺序代码 [crayon-64256f5bc6dfd381839007/] operation2是否需要上一行操作的结果对如何转换成RxJava的代码有很大的影响。 对于第一种有依赖关系的代码来说,需要使用flatMap [crayon-64256f5bc6dff032382945/] 因为有依赖关系,operation2不能与operation1同时进行。 相反,如何operation2不依赖operation1的结果的话,理论上可以同时执行。 比如说上面第二类顺序代码 [crayon-64256f5bc6e01527111424/] 这里通过zip让两个操作同时进行。这是一般顺序代码难以表达的结构。 不过,仍旧存在一类代码,虽然两个操作没有直接依赖关系,但是隐藏着第一个操作失败之后,第二个操作不应该执行的错误依赖要求。对于这类代码,就不应该使用zip让两个操作并行,你需要退回flatMap的方式。 分支结构 分支结构相对简单,比如说最常见的单个if [crayon-64256f5bc6e03897732635/] if加else [crayon-64256f5bc6e04573723145/] if加else if [crayon-64256f5bc6e06968304234/] 当然还有switch表达式,由于switch可以用if来替换,这里不再展开 不管是哪种if,在RxJava里都必须转换为一个有返回值的表达式才能使用。比如说 [crayon-64256f5bc6e07355621037/] 用RxJava的话 [crayon-64256f5bc6e08098298521/] 可以看到这里使用了flatMap,并且if判断被放到了flatMap内部。 和顺序结构时用的flatMap不同,顺序结构时的flatMap主要是考虑到数据依赖,以及异常情况的fail fast,分支结构时用的if,既可以抛出异常,也可以返回不同的值。 抛出异常的例子 [crayon-64256f5bc6e0a806931463/] 改写为RxJava风格 [crayon-64256f5bc6e0b792616366/] 这里由于a来自一个Single流,使用flatMap串了起来。如果a只是一个普通方法参数,直接写if判断可能更好,不是所有时候都需要改成异步。 返回不同的值,也比较简单,看情况你可以使用map而不是flatMap。比如说开始的例子 [crayon-64256f5bc6e0d200917971/] 可以改成 [crayon-64256f5bc6e0e329828462/] 甚至更短 [crayon-64256f5bc6e0f601561288/] 如果没有异常,或者一次肯定返回一个值的话,用map,并且在map里加上判断逻辑即可。 需要注意的是,在写同步代码的时候,有一种风格是优先把异常情况排除掉,在转换为RxJava风格时你可能需要灵活处理,包括使用异常,使用中间结果等等。比如对下面的代码 [crayon-64256f5bc6e11355147662/] 在转换的时候,你你可以选择设计一个特殊的异常用于区分,也可以把statusCode直接作为结果 [crayon-64256f5bc6e12200770776/] 循环结构 接下来一个控制结构是循环。一般的循环转换为RxJava都很简单。比如说 [crayon-64256f5bc6e14281024102/] 如果有条件,可以通过filter/takeWhile等 循环的目的是聚合,比如说计算总和的话,可以用reduce 因为函数式语言本身在这块支持比较丰富,表达性也比同步代码要清晰 […]

  • 【C++11】异步执行之既有函数的包装:packaged_task类和async方法

    上篇中讲到,C++11的标准库提供了promise用于在线程执行的具体方法中返回数据,接收端通过future阻塞获取。这么做的前提是你可以修改方法的参数,或者说你需要写一个包装函数。想要让既有函数异步的话,你可以使用packaged_task类或者async方法。 具体分析之前,以下代码是在线程中需要执行的方法。 [crayon-64256f5bc7222356309423/] MyString是很早之前自己用来查看copy/move次数的类,不想用的话,可以替换为std::string。 packaged_task packaged_task是一个封装了被调用的函数的task。注意,packaged_task本身并不提供异步执行的机制,所以你仍旧需要把packaged_task放到thread中去执行。 [crayon-64256f5bc7226406413056/]

  • 【C++11】基于std::thread异步执行时的输入输出

    本篇主要是记录自己在学习C++11下std::thread异步执行时的一些细节性的东西,为之后基于C++11写并发代码打基础。 C++11引入了std::thread。据说之前因为需要区分对待pthread和win下的线程库,代码中有大量的预编译的if else,非常丑陋。现在的话,统一用std::thread就行了。 基于std::thread最简单的异步执行代码。 [crayon-64256f5bc747f729956357/]

  • 【C++11】字符串与常用数据结构

    学习一门编程语言,考察编程语言支持的基本数据结构是很重要的。如果你以前学的C/C++倾向于自己造轮子,或者你有其他语言背景的话,建议重新了解一下C++11 标准库中的数据结构。 字符串 std::string 你可以用 const char* 也就是字符串字面量来构造 std::string ,也可以从 std::string 中获取 C风格字符串的指针(const char*)。 [crayon-64256f5bc75e1810268292/] std::string 是可变的,所以你可以修改 std::string 而不用太担心性能 [crayon-64256f5bc75e5885759347/] 关于不可变字符串,有很多讨论,这里列举一下想要用不可变的“字符串”话,在不用其他库的情况下可以怎么做 const char* 如果自己分配的字符串数组的话,需要记得delete。字符串字面量的话不用担心。 const std::string& 给 std::string 加const,严格来说这只是防止修改 自己造轮子 std::string 支持 copy 和 move std::string 的 substr 返回的是 copy 过的子字符串。 C++17开始支持 string_view。在C++17之前想用“字符串视图”的话,你可能要自己构造一个类似下面这种包装结构 [crayon-64256f5bc75e7051621322/] std::string 的 = 是字符串比较,而不是地址比较。 std::string 提供的相关方法不多,现有的比如查找find/rfind [crayon-64256f5bc75e8743521345/] 需要注意找不到时返回的不是-1,而是npos,一个特殊的值 std::string 提供了两个获取字符的方法,at 和 […]

  • 【C++11】字符串拼接之回归原点

    在其他语言里,字符串拼接可能是一个常见而且基本不会去注意的部分,但是在C++中字符串拼接有非常多的解决方法。造成这种现象的原因是,C++程序员想要高效地拼接字符串。 比如说下面的代码 [crayon-64256f5bc77d3636434015/] 对于有非C/C++语言的人来说可能最平常不过的代码,C++程序员可能直觉上不会采用这种写法。那么C++里面该用什么写法呢?或者说最佳实践是什么? 这里不会列举各种字符串拼接的方式,如果你有兴趣可以在StackOverflow上搜搜看。个人想要说的是:在分析了C++11里字符串的操作之后个人给出的结论:C++11里最佳的字符串拼接其实就是上述写法。以下是具体分析。

  • 【C++11】move构造函数和std::move

    如果说新的语言特性使得过去的最佳实践不再成立的话,我想move构造函数和std::move所代表的move语义应该算其中一个。 在解释move引起的变化之前,这里先定义一个支持自定义move操作的类 [crayon-64256f5bc7a3c427374037/]

  • 【C++11】从std::string str = “foo”说开去

    最近因为某些原因决定重新开始学习C++。考虑到自己在大学里面学到的C++有点旧(估计是C++98),所以打算从C++11开始。 C++11如其名,是2011年出来的标准,所以2011年之后才有编译器实现。现在2019年大部分PC以及服务器应该都支持C++11了。 个人习惯于看书来学习某样东西,所以找了C++相关书的资料。一开始在o’reilly上找,发现很多书都比较旧。虽然有Effective C++以及More Effective C++系列,但对于初学者来说还不是时候。最后在Stackoverflow上找到了一个比较全的推荐书列表 https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list

  • Java并发研究 自己写ReentrantLock和ReentrantReadWriteLock(4)

    接上篇。在写完ReentrantLock之后,其实可以基于ReentrantLock写一个ReadWriteLock,《the art of multiprocessor programming》第八章有介绍。但是,本着不完全AQS(AbstractQueuedSynchronizer)介绍的系列主题,这里从零开始重新写一个ReentrantReadWriteLock。 按照ReadWriteLock的定义,任何时候都满足 没有线程持有锁 有1~n个线程持有共享锁(Read) 有1个线程持有独占锁(Write) 中的一个。 其次公平的ReadWriteLock要求新来的Read或者Write线程必须在队列中等待,非公平的ReadWriteLock允许新来的Read或者Write比队列中等待的线程先获取锁。关于非公平锁这里多说一句,理论上的非公平锁类似一群人哄抢的现象,但是实现多半是只允许新来和线程队列最前面的线程抢占锁。ReadWriteLock也是一样。如果你想要完全非公平的锁的话,可能AQS和这里的实现不满足你的需求。 为了实现ReadWriteLock的定义,你需要分别记录读写状态。考虑到独占(Write)状态只可能有一个线程,可能场景如下:

  • raspberry pi 3 b+ cross compile on macos mojave

    本文记录了如何在mojave上构建raspberry pi 3 b+的交叉编译环境。交叉编译的原因最主要的还是速度,当然还有目标平台没有直接的编译工具链等。在构建交叉编译环境之前,最好确认目标平台的相关信息,能直接确认目标平台的工具链是最好的,因为从CPU判断可能并不准确。 比如raspebrry pi 3 b+,自带gcc,相关信息如下 [crayon-64256f5bc8429795259042/] 注意其中target为arm-linux-gnueabihf。假如从raspberry pi 3 b+的CPU(Broadcom BCM2837 64bit CPU)判断的话,armv8,aarch64貌似都可以用,但实际用aarch64架构编译出来的程序无法在raspberry pi,准确来说是安装了raspbian上的raspberry pi上执行,这点请注意(假如想在raspberry pi 3 b+上执行aarch64架构的程序的话,个人觉得可能要换raspbian以外的系统,或者修改内核编译选项,这块等自己有空尝试并成功了再发出来)。

  • Compile and install valgrind on macOS Mojave (10.14.2)

    本文是给想在Mojave上编译安装valgrind的人一个参考。 个人因为《Hands on concurrency with Rust》这本书的原因,需要安装valgrind。但是现在(2019/2/9)稳定版本的valgrind尚未支持Mojave,即不能通过Homebrew安装。valgrind的bug tracker里有这个问题的追踪, 但是看状态估计离正式发布还需要时间(开源项目常有的事情,缺少资源,哎)。对话中给了一个github上的commit,看起来可以使用。 下载对应的repository,checkout到修改版的branch上 [crayon-64256f5bc8821272465194/] 按照valgrind自身网站上的说明,接下来是常规的compile install。 [crayon-64256f5bc8825109143929/] 如果你按照顺序执行的话,在make可能会碰到如下两个问题,所以在执行之前,建议先看一下可能碰到的问题 1. No rule to make target `/usr/include/mach/mach_vm.defs’ 简单来说,就是没有找到定义文件。按照stackoverflow上一个问题的说法,你可以通过 [crayon-64256f5bc8826134968343/] 解决,但是答案针对的不是Mojave,所以Mojave除了通过上述命令安装XCode之外,还需要解答中另外一个解决方案,即修改coregrind/Makefile(此文件在./configure之后生成)中mach_vm.defs的路径,具体如下 [crayon-64256f5bc8828362089456/] 也就是在原本 /usr/include/mach/mach_vm.defs 等文件前加上 /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk 前缀,你可以 ls 一下看一下文件是否存在。 2. vgpreload_core_x86_darwin_so-vg_preloaded.o ld: symbol(s) not found for architecture i386 这个问题比较隐蔽,google搜不出来什么东西。直接看错误信息的话,大概知道MacOS下无法链接i386的程序,因为比较新的MacOS基本上只有x86_64。直接的解决方法是不让valgrind去编译i386架构的程序。 如果你留意了 ./configure 最后的输出的话,可以看到Secondary build arch [crayon-64256f5bc882a657467750/] 假如Secondary build arch不像上面那样为空的话,比如是i386,那么make时会出现以上问题。 在 configure 文件中,valgrind的注释提到在MacOS下会同时编译i386和x86_64,这是问题的根本原因,或者说valgrind在Mojave下编译的坑。幸好 configure […]