星期日, 七月 29, 2007

python list pop in loop

如果在一个列表中循环时做了 pop 操作,会有什么问题呢:
>>> L = ['a', 'b', 'c', 'd']
>>> for i in range(len(L)):
... x = L[i]
... if x == 'b': L.pop(i)
...
'b'
Traceback (most recent call last):
File "", line 2, in ?
IndexError: list index out of range
这里已经报错了。但如果换一种方法呢?
>>> L = ['a', 'b', 'c', 'd']
>>> for x in L:
... if x == 'b':
... i = L.index(x)
... L.pop(i)
...
'b'
>>> print L
['a', 'c', 'd']
这里看上去是对的,但会有潜在的问题?例如这样的情况:

>>> L = ['La', 'Lb', 'c', 'Ld']
>>> for x in L:
... if x[0] == 'L':
... i = L.index(x)
... L.pop(i)
...
'La'
'Ld'
>>> print L
['Lb', 'c']

>>> L = ['La', 'Lb', 'c', 'Ld']
>>> for x in L:
... if x[0] == 'L':
... L.remove(x)
...
>>> print L
['Lb', 'c']
那么如果是在多进程环境中呢?

看这个:
#!/usr/bin/env python
# -*- encoding: utf-8 -*-

import os,time

L = [ chr(i+97) for i in range(7) ]
print L
pid = os.fork()
if pid == 0:
for x in L:
print x
time.sleep(3)
print "Child process end"
print "Child List:", L
else:
L.extend(['x', 'y', 'z'])
j = 5
L.pop(j)
print "Main process end"
print "Main List:", L
其运行结果如下:
roc@crablfs:~/ulfs/cutils/trunk/prototypes$ python Ltest.py
['a', 'b', 'c', 'd', 'e', 'f', 'g']
a
Main process end
Main List: ['a', 'b', 'c', 'd', 'e', 'g', 'x', 'y', 'z']
roc@crablfs:~/ulfs/cutils/trunk/prototypes$ b
c
d
e
f
g
Child process end
Child List: ['a', 'b', 'c', 'd', 'e', 'f', 'g']
可以看到子进程没有受到影响,因为进程的地址空间是复制的,如果是线程 thread,就不存在这个问题。

于是,对于 mirrod/fs_mirrod 项目来说,因为 inotify 的 WatchManager 必须共享(父进程/线程写入,子进程/线程读取并根据这个变动的 manager 来建立 Processor),所以如果使用进程解决方案,就必须考虑共享问题;同时共享问题包括一个对资源的锁定问题,即父进程对共享内容的变更可能导致服务子进程/线程读取到的信息不准确!

没有评论: