Параллельные задачи, детерминированные генерацией псевдослучайных чисел - PullRequest
0 голосов
/ 10 ноября 2018

У меня есть сопрограмма, которая возвращает список псевдослучайных байтов

import asyncio
import random
import os

async def random_bytes():
    random.seed(a=1234)
    results = []
    for _ in range(0, 128):
        await asyncio.sleep(int(os.urandom(1)[0])/4096)
        results.append(random.getrandbits(8))
    return results

loop = asyncio.get_event_loop()
task_1 = loop.create_task(random_bytes())
print(loop.run_until_complete(task_1))

Как и ожидалось, этот список всегда одинаков при каждом запуске, хотя интервал времени между каждым поколением различен [на основе некоторой внешней энтропии из os.urandom], генератор псевдослучайных чисел засевается с тем же значением .

Теперь, если я запускаю два из них одновременно, создаю два списка ...

loop = asyncio.get_event_loop()
task_1 = loop.create_task(random_bytes())
task_2 = loop.create_task(random_bytes())
print(loop.run_until_complete(asyncio.gather(task_1, task_2)))

... списки всегда разные: задачи в основном мешают друг другу и больше не являются детерминированными.

Как можно создать две параллельные задачи с псевдослучайными числами, где каждый список псевдослучайных чисел так же детерминирован, как если бы не выполнялась никакая другая задача?

[Мой вариант использования: тестирование параллельного поведения с использованием большого количества псевдослучайных чисел, сгенерированных с недетерминированными интервалами, но хотелось бы, чтобы сами псевдослучайные числа были одинаковыми для каждого прогона теста для каждой задачи ]

Ответы [ 2 ]

0 голосов
/ 10 ноября 2018

Из random модуля документов :

Функции, предоставляемые этим модулем, на самом деле являются связанными методами скрытого экземпляра класса random.Random. Вы можете создать свои собственные экземпляры Random, чтобы получить генераторы, которые не разделяют состояние.

async def random_bytes():
    generator = random.Random()
    generator.seed(1234)
    results = []
    for _ in range(0, 128):
        await asyncio.sleep(int(os.urandom(1)[0])/4096)
        results.append(generator.getrandbits(8))
    return results
0 голосов
/ 10 ноября 2018

Вы можете использовать random.getstate и random.setstate, гарантируя, что между установкой состояния, генерацией случайных чисел и получением состояния вы не уступайте другому заданию.

async def random_bytes():
    random.seed(a=1234)
    state = random.getstate()
    results = []
    for _ in range(0, 128):
        await asyncio.sleep(int(os.urandom(1)[0])/4096)
        random.setstate(state)
        results.append(random.getrandbits(8))
        state = random.getstate()
    return results
...