假设我们从某处接收命令, 一次只能接收一个. 如果命令为 1, 我们只要祈祷, 就可以得到金钱. 如果命令为 2, 那么我们必须到外面 ATM 机取钱, 然后放入钱包.
这个模型可以用下面的计算机伪代码描述:
while(cmd = read_cmd()){ switch(cmd){ case 1: m = pray(); wallet.put(m); break; case 2: m = get_money_from_atm(); wallet.put(m); break; } }
从直觉可以知道, get_money_from_atm() 耗时可能达到 1 个小时, 但是 pray() 只需要几秒. 上面的程序是线性的, 所以 get_money_from_atm() 会影响我们接收新的命令, 从而影响我们的钱包快速的增加. 这是一个严重的问题! 下面将讨论如何解决这个问题.
我们可以委托其他人帮我们去 ATM 机取钱. 所以,
m = get_money_from_atm(); wallet.put(m);
可以改为
delegate(){ m = get_money_from_atm(); wallet.put(m); }
delegate 代码段中的代码会立即被加入到异步执行队列中(在计算机中常常由其它的线程处理这个队列), 所以我们又可以去接收命令了.
问题是, wallet.put() 不是线程安全的, 所以, 调用它时必须加锁. 所以代码应该是
delegate(){ m = get_money_from_atm(); lock(wallet){ wallet.put(m); } }
wallet.put(m);
也要修改为
lock(wallet){ wallet.put(m); }
最终的代码是
while(cmd = read_cmd()){ switch(cmd){ case 1: m = pray(); lock(wallet){ wallet.put(m); } break; case 2: // 这段代码交给另一个线程执行, 所以它几乎不消耗当前线程的时间. delegate(){ m = get_money_from_atm(); lock(wallet){ wallet.put(m); } } break; } }
这里还有很大的改进空间, case 1 情况比 case 2 出现的机率大得多, 很多时候并没有其它的线程争用 wallet, 但我们还是对它进行加锁, 这些加锁都是无谓的. 我想, 能不能达到下面的效果:
while(cmd = read_cmd()){ switch(cmd){ case 1: m = pray(); wallet.put(m); break; case 2: delegate(){ // 将本代码块中的代码委托给其它线程执行. m = get_money_from_atm(); }then{ // 这里的代码会在委托提交后执行. continue; }onexit{ // 一旦委托的代码执行完毕, 会跳到这里继续执行. wallet.put(m); }// 这段几乎不会消耗当前线程的时间. break; } }
委托调用立即返回, 但是, 一旦委托过程处理完毕, 代码又被插入原来的地方继续执行. 也就是说, 一旦 get_money_from_atm() 在另一个线程中执行, 便会产生一个中断, 并执行 onexit 代码段. 当然, 这个中断的级别不能高到中断其它代码的执行, 如 pray().
有一个可以改进的地方, 就是我们不希望委托的数量过多, 所以为 delegate 增加一个参数, delegate(maxwait=2), 表示如果有 2 个执行中的委托, 便阻塞当前的线程.