ichuan.net

自信打不死的心态活到老

【已更新】python 中线程的 join 会延迟重定向后的标准输出

好久没写博客,原因是比较浮躁,心一直静不下来。今日先用一个小技术文章打破这个僵局。

先看下面程序:

#!/usr/bin/env python
# coding: utf-8
# yc@2011/12/26

import threading, time

def log(msg):
        print msg

def worker(s):
        while True:
                time.sleep(s)
                log('I am alive')

log('Creating worker...')
t = threading.Thread(target=worker, args=(2, ))
t.setDaemon(True)
t.start()
log('Worker started')

t.join()
log('Worker ended')

把这个程序保存为 test.py,然后执行 python test.py,会看到如下输出;

Creating worker...
Worker started
I am alive
I am alive
...

每两秒那个线程会向标准输出打印一句 I am alive。没问题吧?重新来,这样执行:python test.py >/tmp/x,将输出重定向到某文件,之后在另一 shell 窗口中执行 tail -f /tmp/x,预想结果应该是每两秒有输出,然而没有。再看 /tmp/x 文件,竟然是空的。

何解?

原因在于那个 `t.join()` 延迟了主线程重定向后的标准输出

经小 pan 同学点拨,不是 join() 的问题,而是重定向后的文件句柄的缓冲区导致的。解决方法是在 print msg 后调用 sys.stdout.flush()

那把 print msg 换成 sys.stdout.write(msg) 可以么?不行,还是需要刷新缓冲区。不过向标准错误输出写东西是会自动刷新的,可以试试把 print msg 换成 sys.stderr.write(msg)

计划.IN 开源

如同之前承诺的那样,现在将这个项目开源了,欢迎 fork:

https://github.com/ichuan/jihua

jihua.in 这个网站会一直运行,我本身就一直在用。

有个 mm 图在 doc/design.mm,是那晚简单做的一个设计。其他介绍就不写了,都在 github 那里。

计划.IN 上线

最近一周都没写博客,因为我在做一个网站。现在基本成型了,于是放出来,欢迎使用。

计划.IN —— 简单有效的工作计划表

从上周五买域名开始,到刚才把网站部署上去,总共花去了我 10 天的“空闲”时间。平时要上班,哪有那么多空闲时间啊。嗯,我基本都是在熬夜写代码的。10 天里熬夜了几天都记不清了,每次都是最早凌晨 5 点睡,那时睡的话要设 4 个间隔 10 分钟的闹钟,以防上班迟到。期间有天要出差,早上 7 点 50 的火车,那天就没睡,在火车上补的觉。

这么疯狂的原因一个是这是我喜欢的事,自然有无穷的动力去做;另一个也是做这个网站的初衷:我想确信自己还没老去,能力还没消失,我还不是个废人。

这期间又开始喝咖啡了。有些熬夜的晚上,抬起头会有奇异的感觉。后来也看到某百度员工猝死云云,甚惊恐,怕自己哪个晚上也会不小心驾鹤西去,一命呜呼。自毕业后我已有好久没通宵过了,之前玩游戏时偶尔通宵一次也会累个半死,第二天下午睡醒后有世界末日般的感觉。这次还好。

说正题。计划.IN(我这么叫它)其实就是个 todo list + 备忘录工具,具体的介绍可以看帮助页面,刚写的,还热乎着。开发过程可以简单概括为:买域名;用 freemind 列需求和设计;用 mockups 画原型图;实现 ui;实现后台逻辑。

前端 ui 实现占用了我 80% 的开发时间。因为我对细节有洁癖,而且自认有些审美能力,所以一直在细调。logo 是照着 twitter 的用 ps 画的;首页的预览图也是截图后用 ps 旋转得到的。js 代码局部重构了好几次,因为我要的既能运作又看起来漂亮的代码。为了想实现一种动画效果,把 css3 的 transform 尝试了遍,最后以失败告终。前端用的 twitter 的 bootstrap css 框架和核武器 backbone.js MVC 框架,后者的源码也几乎被我看完了。

后台逻辑实现比较简单,主要是一个 RESTful 接口。这个项目里我写的 python 代码远没有写的 js 代码多。现在发现 django 太庞大了,这次又学到很多以前没用过的功能。

因为比较关心数据安全性,于是申请了 StartSSL 的免费 SSL 证书,折腾一会给装上了。

促成我开始做这个站的最初原因是徐东同学说他有 todo list 的需求,然后我想起了我荒废很久的 MyPDC2.0 计划,其中就包含一个类似 计划.IN 的记事功能。于是先把这个实现了,要等想法都成熟才开始干是不明智的。

还有些没实现的功能,但我等不下去了,先让功能可用的它上线吧,我得休息下了。

命令行下的spark画图

创意来源:https://github.com/holman/spark

python 实现:https://github.com/ichuan/yc-lib/blob/master/python/spark.py

使用方法:

将数值以参数传入

admin@test: ~$ python spark.py 0 30 55 80 33 150  
▁ ▂ ▃ ▅ ▂ █

通过管道传入

admin@test: ~$ curl http://earthquake.usgs.gov/earthquakes/catalogs/eqs1day-M1.txt --silent|sed '1d' |cut -d, -f9 |tr "\n" ',' |sed 's/ //g' |python spark.py
▁ ▂ ▂ ▁ ▂ ▁ ▁ ▂ ▁ ▃ ▂ ▂ ▁ ▁ ▂ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▅ ▂ ▁ ▂ ▁ ▁ ▁ ▅ ▁ ▂ ▂ ▅ ▂ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▂ ▁ ▃ ▁ ▁ ▁ ▁ ▄ ▂ ▃ ▁ ▄ ▁ ▂ ▁ ▁ ▁ ▁ ▁ ▅ ▁ ▁ ▁ ▄ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▂ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▂ ▂ ▂ ▂ ▁ ▁ ▁ ▂ ▅ ▁ ▁ ▁ ▂ ▅ ▄

python小工具:tcp proxy和tcp hub

刚才看书,忽然想用 pythonsocket 模块写个 tcp proxy 工具,于是立马动手。写完后运行,改了几个手误,竟再无严重 bug。这点让我很欣慰。

完成的脚本是:tcp_proxy.py。这个脚本作用就是让一台机器变成 tcp 跳板。例如 A 机器想连 B 机器,但不能直接连上;而 C 机器可以直接连 B 机器,也可以与 A 机器连接。则在 C 机器上执行此脚本,A 机器连 C 机器的一个端口,就相当于连到 B 机器的某个端口,间接实现 AB 的通信。这和 HTTP 代理类似。

这个脚本支持多客户端同时连接。

以下是一个在代理机上执行此脚本后,我连接代理机 1234 端口的输出:

$ python tcp_proxy.py -l 1234 -r baidu.com:80 -v
Listening at 0.0.0.0:1234 ...
New clients from 10.2.3.5:56344
10.2.3.5:56344 => 220.181.111.85:80 (481 bytes)
220.181.111.85:80 => 10.2.3.5:56344 (381 bytes)
10.2.3.5:56344 => 220.181.111.85:80 (406 bytes)
New clients from 10.2.3.5:56348
220.181.111.85:80 => 10.2.3.5:56344 (551 bytes)
10.2.3.5:56348 => 123.125.114.144:80 (593 bytes)
123.125.114.144:80 => 10.2.3.5:56348 (197 bytes)
New clients from 10.2.3.5:56352
10.2.3.5:56352 => 220.181.111.86:80 (136 bytes)
220.181.111.86:80 => 10.2.3.5:56352 (381 bytes)
Socket closed by 10.2.3.5:56352
Socket closed by 220.181.111.85:80
Socket closed by 123.125.114.144:80
^CClosing...

上例中 10.2.3.5 相当于 A 机器,而 baidu.com 相当于 B 机器。

测试了代理 ssh 连接,无问题。

还有一种情况是:C 也无法连接 B,但 B 能连接 C。比如 AB 都是处在不同内网的机器,C 是公网上的一台机器。这样就需要 AB 分别连接 CC 然后把两个 socket 中转。

针对这种情况,我改了下原先的脚本,得到一个新的脚本:tcp_hub.py

以下是在公网机器上执行此脚本后的输出。我另外在两个内网机器上分别用 nc 去连公网机器的 1234512346 端口,连接后两个内网机器可以相互 echo 信息:

$ python tcp_hub.py -a 12345 -b 12346 -v
Listening at 0.0.0.0:12345 ...
Listening at 0.0.0.0:12346 ...
New clients from 128.224.233.142:35595 ...
New clients from 128.224.233.142:33668 ...
128.224.233.142:33668 => 128.224.233.142:35595 (2 bytes)
128.224.233.142:35595 => 128.224.233.142:33668 (5 bytes)
Socket closed by 128.224.233.142:33668
Socket closed by 128.224.233.142:35595