当一个人返回结果时,Python会停止多个进程吗?

mes*_*ria 15 python cryptography sha256 nonce python-multiprocessing

我试图在python中编写一个简单的工作证明nonce-finder.

def proof_of_work(b, nBytes):
    nonce = 0
    # while the first nBytes of hash(b + nonce) are not 0
    while sha256(b + uint2bytes(nonce))[:nBytes] != bytes(nBytes):
        nonce = nonce + 1
    return nonce
Run Code Online (Sandbox Code Playgroud)

现在我尝试进行多处理,因此它可以使用所有CPU内核并更快地找到nonce.我的想法是multiprocessing.Pool多次使用和执行函数proof_of_work,传递两个参数num_of_cpus_running,this_cpu_id如下所示:

def proof_of_work(b, nBytes, num_of_cpus_running, this_cpu_id):
    nonce = this_cpu_id
    while sha256(b + uint2bytes(nonce))[:nBytes] != bytes(nBytes):
        nonce = nonce + num_of_cpus_running
    return nonce
Run Code Online (Sandbox Code Playgroud)

所以,如果有4个核心,每个核心将计算这样的随机数:

core 0: 0, 4, 8, 16, 32 ...
core 1: 1, 5, 9, 17, 33 ...
core 2: 2, 6, 10, 18, 34 ...
core 3: 3, 7, 15, 31, 38 ...
Run Code Online (Sandbox Code Playgroud)

所以,我必须重写proof_of_work所以当任何进程找到一个nonce时,其他人都停止查找nonce,考虑到找到的nonce必须是所需字节为0的最低值.如果CPU加速由于某种原因,并返回高于最低有效nonce的有效nonce,则工作证明无效.

我唯一不知道怎么做的是,如果进程B发现一个nonce低于现在由进程A计算的nonce,进程A只会停止.如果它更高,A保持计算(以防万一)直到它到达由B提供的随机数.

我希望我能正确解释自己.此外,如果我所写的任何内容的执行速度更快,我很乐意听到它.非常感谢你!

Maa*_*wes 6

执行此操作的一般方法是:

  1. 考虑工作包,例如为了执行特定范围的计算,范围不应该花费很长时间,例如0.1秒到一秒
  2. 让一些经理将工作包分发给工人
  3. 工作包结束后,告诉管理员结果并请求新的工作包
  4. 如果工作完成并且结果已经发现接受工人的结果并给他们一个信号,表明不再需要工作 - 工人现在可以安全地终止

这样你就不必每次迭代检查管理器(这会减慢一切),或者做一些讨厌的事情,比如在会话中停止一个线程.不用说,经理需要线程安全.

这非常适合您的模型,因为您仍然需要其他工作人员的结果,即使已找到结果.


请注意,在您的模型中,可能是线程可能与其他线程不同步,落后.一旦找到结果,您不希望再进行一百万次计算.我只是从问题中重申这一点,因为我认为这个模型是错误的.您应该修复模型而不是修复实现.

  • 新手错误:给经理一个低优先级.由于经理在大多数时间没有真正做任何事情,它应该优先*至少与工人一样高*,但最好是更高.否则,工作人员可能必须永远等待新数据包.是的,我曾经是新手一次:) (2认同)
  • 更加灵活.添加/删除工作人员,更改工作包的大小,为用户添加选项以指示何时停止等. (2认同)

Nik*_*yrh 5

一个简单的选择是使用微批并检查是否找到了答案.由于启动并行作业会导致批量过小,因此过大的大小会导致其他进程执行额外的工作,而一个进程已经找到了答案.每批应该花费1到10秒才能有效.

示例代码:

from multiprocessing import Pool
from hashlib import sha256
from time import time


def find_solution(args):
    salt, nBytes, nonce_range = args
    target = '0' * nBytes

    for nonce in xrange(nonce_range[0], nonce_range[1]):
        result = sha256(salt + str(nonce)).hexdigest()

        #print('%s %s vs %s' % (result, result[:nBytes], target)); sleep(0.1)

        if result[:nBytes] == target:
            return (nonce, result)

    return None


def proof_of_work(salt, nBytes):
    n_processes = 8
    batch_size = int(2.5e5)
    pool = Pool(n_processes)

    nonce = 0

    while True:
        nonce_ranges = [
            (nonce + i * batch_size, nonce + (i+1) * batch_size)
            for i in range(n_processes)
        ]

        params = [
            (salt, nBytes, nonce_range) for nonce_range in nonce_ranges
        ]

        # Single-process search:
        #solutions = map(find_solution, params)

        # Multi-process search:
        solutions = pool.map(find_solution, params)

        print('Searched %d to %d' % (nonce_ranges[0][0], nonce_ranges[-1][1]-1))

        # Find non-None results
        solutions = filter(None, solutions)

        if solutions:
            return solutions

        nonce += n_processes * batch_size


if __name__ == '__main__':
    start = time()
    solutions = proof_of_work('abc', 6)
    print('\n'.join('%d => %s' % s for s in solutions))
    print('Solution found in %.3f seconds' % (time() - start))
Run Code Online (Sandbox Code Playgroud)

输出(配备Core i7的笔记本电脑):

Searched 0 to 1999999
Searched 2000000 to 3999999
Searched 4000000 to 5999999
Searched 6000000 to 7999999
Searched 8000000 to 9999999
Searched 10000000 to 11999999
Searched 12000000 to 13999999
Searched 14000000 to 15999999
Searched 16000000 to 17999999
Searched 18000000 to 19999999
Searched 20000000 to 21999999
Searched 22000000 to 23999999
Searched 24000000 to 25999999
Searched 26000000 to 27999999
Searched 28000000 to 29999999
Searched 30000000 to 31999999
Searched 32000000 to 33999999
Searched 34000000 to 35999999
Searched 36000000 to 37999999
37196346 => 000000f4c9aee9d427dc94316fd49192a07f1aeca52f6b7c3bb76be10c5adf4d
Solution found in 20.536 seconds
Run Code Online (Sandbox Code Playgroud)

单核心需要76.468秒.无论如何,这不是找到解决方案的最有效方法,但它有效.例如,如果salt长时间,那么SHA-256可以在吸收盐之后预先计算状态,并从那里继续进行强力搜索.字节数组也可能比hexdigest().

  • 并行性的出色表现。然而,它是否回答了OP的问题:一旦一个线程找到答案,如何停止其他线程? (2认同)