V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
40huo
V2EX  ›  Python

Python 多进程操作文件,文件锁好像并没有生效

  •  
  •   40huo · 2017-08-24 12:46:20 +08:00 · 6134 次点击
    这是一个创建于 2639 天前的主题,其中的信息可能已经有所发展或是发生改变。
    import multiprocessing
    import json
    import time
    import fcntl
    
    
    def init():
        with open('list.txt', 'w') as f:
            f.write(json.dumps({'sids': {}}))
    
    
    def list(data=None):
        if data is None:
            with open('list.txt', 'r') as f:
                fcntl.lockf(f.fileno(), fcntl.LOCK_EX)
                print(f.read())
                return json.loads(f.read())
        else:
            with open('list.txt', 'w') as f:
                fcntl.lockf(f.fileno(), fcntl.LOCK_EX)
                f.write(json.dumps(data))
    
    
    def generate(sid):
        target = 'http://www.baidu.com'
        data = list()
        data['sids'][sid] = target
        print(len(data['sids']))
        list(data)
        time.sleep(5)
    
    
    if __name__ == '__main__':
        init()
        processes = []
        for i in range(100):
            p = multiprocessing.Process(target=generate, args=(i,))
            processes.append(p)
            p.start()
    
        for p in processes:
            p.join()
    

    几乎所有进程都报 json 解码错误

        raise ValueError("No JSON object could be decoded")
    ValueError: No JSON object could be decoded
    

    这是哪里出了问题?

    第 1 条附言  ·  2017-08-24 18:16:42 +08:00

    放一个目前没问题的。

    import multiprocessing
    import json
    import time
    import fcntl
    
    
    def init():
        with open('list.txt', 'w') as f:
            f.write(json.dumps({'sids': {}}))
    
    
    def list_tmp(data=None):
        if data is None:
            with open('list.txt', 'r') as f:
                fcntl.flock(f, fcntl.LOCK_EX)
                data = f.read()
                return json.loads(data)
        else:
            with open('list.txt', 'r+') as f:
                fcntl.flock(f, fcntl.LOCK_EX)
                text = f.read()
                text = json.loads(text)
                text['sids'][data[0]] = data[1]
                f.seek(0)
                f.truncate()
                f.write(json.dumps(text))
    
    
    def generate(sid):
        target = 'http://www.baidu.com'
        data = (sid, target)
        list_tmp(data)
        time.sleep(1)
    
    
    if __name__ == '__main__':
        init()
        processes = []
        for i in range(100):
            p = multiprocessing.Process(target=generate, args=(i,))
            processes.append(p)
            p.start()
    
        for p in processes:
            p.join()
    
        with open('list.txt', 'r') as f:
            data = f.read()
            data = json.loads(data)
            print(len(data.get('sids')))
    

    之前的代码读入旧数据不应该在写的部分的外面,这样多进程读入的本来就是不全的数据,另外 seektruncate 也有很明显的作用,原因不知。。。

    15 条回复    2017-08-24 17:25:23 +08:00
    wayslog
        1
    wayslog  
       2017-08-24 13:37:30 +08:00
    data['sids'][sid] = target
    print(len(data['sids']))

    sid from ????
    xfwduke
        2
    xfwduke  
       2017-08-24 13:50:09 +08:00
    试试 list 函数直接把 f 返回出去, 并且让外部一直持有这个对象
    之前碰到过类似的问题. 写了个

    def lock (filename) 这样的函数
    一直不生效

    应该是在函数内部打开文件, 文件对象在函数完成后就关闭了. 锁也释放了
    xfwduke
        3
    xfwduke  
       2017-08-24 13:54:22 +08:00
    ```python
    def set_lock(block=False):
    lock_f = open(os.path.join(os.path.dirname(os.path.abspath(sys.argv[0])),
    '.{0}.lock'.format(os.path.splitext(os.path.basename(sys.argv[0]))[0])), 'w')
    if block:
    fcntl.flock(lock_f.fileno(), fcntl.LOCK_EX)
    else:
    fcntl.flock(lock_f.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
    return lock_f
    ```
    a87150
        4
    a87150  
       2017-08-24 14:00:24 +08:00
    Python 3.6.2 (v3.6.2:5fd33b5, Jul 8 2017, 04:57:36) [MSC v.1900 64 bit (AMD64)] on win32
    Type "help", "copyright", "credits" or "license" for more information.
    >>> data = list()
    >>> target = 'http://www.baidu.com'
    >>> data['sids'][0] = target
    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    TypeError: list indices must be integers or slices, not str

    直接报错了,真的能这么写吗
    wwqgtxx
        5
    wwqgtxx  
       2017-08-24 14:01:28 +08:00
    我怎么觉得问题在这里
    print(f.read())
    return json.loads(f.read())
    你读了两遍,写的时候却只写了一遍
    zhangysh1995
        6
    zhangysh1995  
       2017-08-24 14:05:29 +08:00
    lockf -> flock 试试,参考 https://stackoverflow.com/questions/28470246/python-lockf-and-flock-behaviour

    你的 lock 用错了。lockf 对每一个人进程的 file descriptor 都是独立的,所以没用。
    官方文档: https://docs.python.org/3/library/fcntl.html
    40huo
        7
    40huo  
    OP
       2017-08-24 14:06:18 +08:00
    @wwqgtxx #5 是有这个问题,去掉 print 后有少部分进程报 raise ValueError("No JSON object could be decoded")错误
    40huo
        8
    40huo  
    OP
       2017-08-24 14:07:34 +08:00
    @a87150 #4 list 是我自定义的函数,不是定义了一个列表。。。命名问题是我的锅。。。函数返回的是一个字典。
    40huo
        9
    40huo  
    OP
       2017-08-24 14:08:58 +08:00
    @zhangysh1995 #6 试过了,一样的问题
    zhangysh1995
        10
    zhangysh1995  
       2017-08-24 14:11:17 +08:00
    @wwqgtxx 刚才试了一下,这句话确实有问题,第二遍的时候 buffer 应该是空了
    40huo
        11
    40huo  
    OP
       2017-08-24 14:24:41 +08:00
    @zhangysh1995 #10 本来应该写 100 个,现在每次都有那么 4,5 个写失败。。。感觉这文件锁和假的一样。。。
    topbandit
        12
    topbandit  
       2017-08-24 14:56:35 +08:00
    file lock 在打开文件之前获取,你把 fcntl.lockf(f.fileno(), fcntl.LOCK_EX)提到 open file 前面
    wwqgtxx
        13
    wwqgtxx  
       2017-08-24 15:03:12 +08:00 via iPhone
    @topbandit 你在 open 之前根本就没有 f 这个对象,怎么 lock
    lolizeppelin
        14
    lolizeppelin  
       2017-08-24 17:22:07 +08:00 via Android
    直接 fork 不要用库试试
    virusdefender
        15
    virusdefender  
       2017-08-24 17:25:23 +08:00 via iPhone
    6 楼正解
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3717 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 05:04 · PVG 13:04 · LAX 21:04 · JFK 00:04
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.