2

I have a written a class to process data from a specific instrument. Each instance of the class contains a different example of this data. An equivalent toy example of this is given in the code below, which is a modified/mutilated version of this example:

import pickle as pkl
from multiprocessing import Process, Queue, current_process, freeze_support

class TestMaths:
    def __init__(self, a, b):
        self.a = a
        self.b = b
        self.c = None

    def mul(self):
        self.c = self.a * self.b
        return 0

    def plus(self):
        self.c = self.a + self.b
        return 0


def worker(input, output):
    for maths_class in iter(input.get, 'STOP'):
        result = calculate(maths_class)
        output.put(maths_class)


def calculate(maths_class):
    result = maths_class.mul()
    return "calculation returns {0:d}".format(result)


def test():
    NUMBER_OF_PROCESSES = 4
    maths_classes = [TestMaths(ii, 7) for ii in range(20)]
    TASKS1 = [(maths_class) for maths_class in maths_classes]
    TASKS2 = [(maths_class) for maths_class in maths_classes]

    # Create queues
    task_queue = Queue()
    done_queue = Queue()

    # Submit tasks
    for task in TASKS1:
        task_queue.put(task)

    # Start worker processes
    for i in range(NUMBER_OF_PROCESSES):
        Process(target=worker, args=(task_queue, done_queue)).start()

    # Get and print results
    print('Unordered results:')
    for i in range(len(TASKS1)):
        print('\t', done_queue.get().c)

    # Tell child processes to stop
    for i in range(NUMBER_OF_PROCESSES):
        task_queue.put('STOP')


if __name__ == '__main__':
    freeze_support()

    # confirm pickleable
    test_pickle = TestMaths(2, 2)
    test_pickle.mul()
    print("{0:f} x {1:f} = {2:f}".format(test_pickle.a, test_pickle.b, test_pickle.c))
    pkl.dump(test_pickle, open("test.pkl", "wb"))
    test_pickle_returned = pkl.load(open("test.pkl", "rb"))
    print("{0:f} x {1:f} = {2:f}".format(test_pickle_returned.a, test_pickle_returned.b, test_pickle_returned.c))

    # parallelisation
    test()

The first block of code under the main if statement is supposed to confirm that the class is pickleable. After this the call to the function test runs the actually toy problem. When I run this with Python 3.6.6 on Windows 7 I get the following error:

Traceback (most recent call last):
  File "C:\Users\rpoolman\Documents\QAS Results\Objective2\analysis\test2.py", line 82, in <module>
    test()
  File "C:\Users\rpoolman\Documents\QAS Results\Objective2\analysis\test2.py", line 62, in test
    print('\t', done_queue.get().c)
  File "C:\Users\rpoolman\WinPython-64bit-3.6.6.1Qt5\python-3.6.6.amd64\lib\multiprocessing\queues.py", line 113, in get
    return _ForkingPickler.loads(res)
AttributeError: Can't get attribute 'TestMaths' on <module '__main__' from 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Professional\\Common7\\IDE\\Extensions\\Microsoft\\Python\\Core\\ptvsd_launcher.py'>

I'm aware the class needs to be Pickeable and think that TestMaths is. However, it seems that this error relates to the multiprocessing module's version of Pickle. Is there a way to refactor my class so that I can use multiprocessing?

RHP
  • 103
  • 7
  • 1
    Cannot reproduce - I don't get an error/exception with posted code. Python 3.4.4. It does print a series of `unordered results` - multiples of seven. – wwii Aug 30 '18 at 16:37
  • If you insert `time.sleep(2)` into `calculate` before you let return, do you still get the error? – Darkonaut Aug 30 '18 at 20:52
  • @wwii- What os are you using? I remember reading that there where difference between windows and apple/linux when using the multiprocessing module. – RHP Aug 31 '18 at 07:53
  • @Darkonaut- I'm afraid adding the delay makes no difference to me. – RHP Aug 31 '18 at 07:54
  • @RHP try with appending the processes to a list before you start them by iterating over the list. – Darkonaut Aug 31 '18 at 11:04
  • Windows. Just tried it at home also and it works: `Python 3.6.0 (v3.6.0:41df79263a11, Dec 23 2016, 08:06:12) [MSC v.1900 64 bit (AMD64)] on win32` – wwii Aug 31 '18 at 14:02
  • @Darkonaut That didn't work and looking at the error message I think that the issue is the retrieval of the class from the child process rather than starting the child process. – RHP Aug 31 '18 at 14:41
  • @wwii I've got two python 3.6.1 environments on this PC one 32-bit and the other 64-bit I get a failure on both with the same error. I'll try the code at home later too and see what happens. – RHP Aug 31 '18 at 14:50
  • @RHP Had something similar in mind. I got a FileNotFoundError on Linux running your script with start method set to 'spawn' (default on Windows). The reason seemingly was the processes terminated to early and the queue somehow went corrupt because of that. Could resolve it with delays or holding a reference to the processes. But it seems you have a different pony here. Chance it has something to do with Visual Studio? – Darkonaut Aug 31 '18 at 14:51
  • @Darkonaut I'm using a [WinPython](https://winpython.github.io/) distribution run with the Spyder IDE and get this error. I also get it when I try to run from Visual Studio 2017, so it seems to be a little broader than that. – RHP Aug 31 '18 at 15:09
  • @RHP Have you tried running it from cmd? – Darkonaut Aug 31 '18 at 15:13
  • @Darkonaut It works! That's a bit unusual. – RHP Aug 31 '18 at 15:19
  • @wwii Are you trying to run the the script from a ipython console or a normal python console? – RHP Aug 31 '18 at 15:20
  • @RHP [spyder](https://stackoverflow.com/a/48099756/9059420) is known for having problems with multiprocessing, didn't know about VS in that regard. – Darkonaut Aug 31 '18 at 15:26
  • @Darkonaut I know it's not really a solution more a workaround but if you add that as an answer I'll accept it. – RHP Sep 03 '18 at 08:00
  • @RHP Thanks but not neccessary from my point. I suggest you contact VS-support directly about this and post their answer. If it doesn't run with VS then, consider switching the IDE. – Darkonaut Sep 03 '18 at 13:47

0 Answers0