代码之家  ›  专栏  ›  技术社区  ›  Pace

多线程python访问似乎是同步的

  •  1
  • Pace  · 技术社区  · 5 年前

    我有一个任务,10个线程每个线程“同时”向一个文件写入100行。我原以为这些字会交错排列相反,写操作似乎是同步的我知道GIL,但我不认为它适用于文件I/O,因为底层OS调用不在GIL之外。

    import threading
    import tempfile
    
    with tempfile.NamedTemporaryFile(delete=False) as named_temp:
    
        temp_filename = named_temp.name
        print(temp_filename)
    
        with open(temp_filename, mode='a') as writer:
    
            def thread_task(writer, thread_index):
                for iter_index in range(0, 100):
                    writer.write(f'{(iter_index + thread_index * 100):06}')
                    writer.write('\n')
    
            def make_thread(writer, thread_index):
                return threading.Thread(target=lambda: thread_task(writer, thread_index))
    
            threads = []
            for thread_index in range(0, 10):
                threads.append(make_thread(writer, thread_index))
    
            for thread in threads:
                thread.start()
            for thread in threads:
                thread.join()
    
        with open(temp_filename, mode='r+') as reader:
            for line in reader.readlines():
                print(line, end='')
    

    这是意料之中的,还是我设置的不正确?我担心上面的代码交错输出(我不介意行的顺序,但不希望像 000007000008\n\n 是的。所以我计划引入锁,但在我这么做之前,我想创建一个失败的测试,我很难这样做。

    这是在Python3.6.8上,如果相关的话。

    另外,我的意思是我的输出是 000001\n000002\n...000999\n 井然有序至少我会预料到编号有问题。

    1 回复  |  直到 5 年前
        1
  •  4
  •   ShadowRanger    5 年前

    问题是写操作是被缓冲的,所以gil实际上并没有被释放(它只在缓冲区实际被写出来时才被释放,通常只有在缓冲区已满或文件显式地 flush ED或 close d)由于每个线程所做的工作非常少,它们永远不会运行足够长的时间来释放gil,因为超时,并且永远不会真正写入磁盘,它们永远不会因为开始阻塞系统调用而释放gil。

    如果你成功了 脸红 对于每一行(或者使缓冲区足够小,以至于在完成所有的 write s),您将看到预期的交错。一种方法是改变:

    with open(temp_filename, mode='a') as writer:
    

    致:

    with open(temp_filename, mode='a', buffering=1) as writer:
    

    在哪里? buffering=1 表示缓存线。