子流程执行的顺序及其对运营原子性的影响

时间:2015-08-24 22:47:32

标签: python multithreading multiprocessing python-multiprocessing

我正在学习python多处理模块,并且我找到了this示例(这是一个有点修改的版本):

#!/bin/env python
import multiprocessing as mp
import random
import string
import time

# Define an output queue
output = mp.Queue()

# define a example function
def rand_string(length, output):
    time.sleep(1)
    """ Generates a random string of numbers, lower- and uppercase chars. """
    rand_str = ''.join(random.choice(
                    string.ascii_lowercase
                    + string.ascii_uppercase
                    + string.digits)
               for i in range(length))
    result = (len(rand_str), rand_str)
    print result
    time.sleep(1)
    output.put(result)


def queue_size(queue):
    size = int(queue.qsize())
    print size


# Setup a list of processes that we want to run
processes = [mp.Process(target=rand_string, args=(x, output)) for x in range(1,10)]


# Run processes
for p in processes:
    p.start()


# Exit the completed processes
for p in processes:
    p.join()


# Get process results from the output queue
results = [output.get() for p in processes]
print(results)

以下内容如下:

(3, 'amF')
(1, 'c')
(6, '714CUg')
(4, '10Qg')
(5, 'Yns6h')
(7, 'wsSXj3Z')
(9, 'KRcDTtVZA')
(2, 'Qy')
(8, '50LpMzG9')
[(3, 'amF'), (1, 'c'), (6, '714CUg'), (4, '10Qg'), (5, 'Yns6h'), (9, 'KRcDTtVZA'), (2, 'Qy'), (7, 'wsSXj3Z'), (8, '50LpMzG9')]

我知道流程不是按照创建它们的顺序调用的(使用processes = [mp.Process(target=rand_string, args=(x, output)) for x in range(1,10)]),这在参考文章中提到过。我不明白(或者我不确定是否理解正确)是为什么result的顺序与打印输出result到STDOUT的顺序不对应的原因?我对此的理解是这三个操作不是原子的(我的意思是它们可以通过进程切换分开):

    print result
    time.sleep(1)
    output.put(result)

基本上这里发生的是,在进程print results到STDOUT的那一刻,它被切换到写入results的另一个进程。这样的事情:

Time 
------------------------------------------------------------------------------------------------------------------>
Process1: print results |               |                                    | time.sleep(1) | output.put(result) |
Process2:               | print results | time.sleep(1) | output.put(result) |               |                    |

在这种情况下,STDOUT上的输出为:

(1, 'c')
(2, 's5')

results的实际内容将是:

[ (2, 's5') (1, 'c')]

出于同样的原因,这些过程不会随着它们的创建而被盯着。

我是对的吗?

2 个答案:

答案 0 :(得分:1)

是的,你是对的 - 进程不会在锁定步骤中执行。现代操作系统使用复杂的算法来决定何时从一个进程切换到另一个进程,但这些算法不会对任何进程提供任何形式的保证,以确定它将如何进展而不是相同优先级的另一个进程(通常只有有限的保证不同的优先事项)。

通常,进程在等待操作系统时或当前时间片(基于硬件中断中断)到期时被阻止。这些都是定期发生的,但是前景任务在勾选期间接收的时间长短取决于后台正在进行的操作,以及何时切换进程(可能是因为另一个进程被切换,因为它在I /上阻塞) O)。

如果您使用不同的系统加载重新运行测试,很可能会得到不同的结果。 (而且每个流程要做的工作越多,你就越有可能看到不同的结果。)

答案 1 :(得分:1)

你是对的。操作系统kernel可以并且将执行上下文切换,并且只要它愿意这样做。 Python解释器(或Just-In-Time编译器或其他)是一个userspace程序,因此完全由内核控制。

这个"内核/用户奴隶制"因此从父亲传给孩子"或者换句话说,Python程序与解释器相关,而解释器又与内核相关。

因此,用户空间程序(例如Python应用程序)可以确保同步的唯一方法是使用锁定原语,例如mutex es或其他synchronization primitives

现在,在现实世界中,通常会导致文件写入中的上下文切换(例如stdout,默认情况下由print完成),昂贵操作的批次应该完成,例如system calls,复杂的内存重映射和黑魔法,以及环回机制(例如当stdout引用pseudo-terminal时,这是今天最常见的情况)。