# Collection of code snippets by Arne Vajhøj 
# (from articles on eksperten.dk / vajhoej.dk written sometime between 2004 and now) 
import multiprocessing
import time

class Data(object):
    def __init__(self, _s):
        self.s = _s

# non-OO as that is much simpler
def run(d, q, k):
    # simulate a lot of work that takes 0.1 second
    s = d.s
    time.sleep(0.1)
    d.s = s + "X"
    # send updated data back
    q.put([k, d])

def test(njobs, nthreads):
    # setup data
    d = []
    for i in range(njobs):
        d.append(Data("X"))
    # process
    t1 = time.time()
    q = multiprocessing.Queue(njobs)
    k = 0
    for i in range(njobs / nthreads): # // in Python 3.x
        # create threads
        t = []
        for j in range(nthreads):
            t.append(multiprocessing.Process(target=run, args=(d[k],q,k,)))
            k = k + 1
        # start threads
        for j in range(nthreads):
            t[j].start()
        # wait for threads to complete
        for j in range(nthreads):
            t[j].join()
    t2 = time.time()
    print("%d jobs executing in %d threads : %.1f seconds" % (njobs, nthreads, (t2 - t1)))
    # get all updated data
    while not q.empty():
        res = q.get()
        d[res[0]] = res[1]
    # check data
    for i in range(njobs):
        if d[i].s != "XX":
            print("Ooops" + d[i].s)

if __name__ == '__main__': # necesaary as copies are forked
    test(128, 1)
    test(128, 2)
    test(128, 4)
    test(128, 8)
    test(128, 16)
    test(128, 32)
    test(128, 64)
