Python multiprocessing `spawn` start method throwing `context has already been set` Runtime error

Viewed 110

I am trying to set the multiprocessing.set_start_method() to spawn mode in my if __name__ == '__main__' code.

Running this on ubunu VERSION="18.04.6 LTS (Bionic Beaver)". I have to set this to spawn mode in order for my pytorch inference to work.

But when i run the program, i am getting the below error.

Error in Main function : %s Traceback (most recent call last):
  File "/home/ubuntu/multi-process-testing/main.py", line 22, in <module>
    set_start_method('spawn')
  File "/home/ubuntu/anaconda3/envs/conda-pv-pytorch-2/lib/python3.9/multiprocessing/context.py", line 243, in set_start_method
    raise RuntimeError('context has already been set')

RuntimeError: context has already been set

When i try to use the force=True, i am getting the leaked semaphores error/warning and my program is not executing.

(conda-pv-pytorch-2) ubuntu@ip-172-31-22-56:~/multi-process-testing$ /home/ubuntu/anaconda3/envs/conda-pv-pytorch-2/lib/python3.9/multiprocessing/resource_tracker.py:216: UserWarning: resource_tracker: There appear to be 2 leaked semaphore objects to clean up at shutdown
  warnings.warn('resource_tracker: There appear to be %d '

My main.py looks like below.

from consumer import ConsumerVideoHandlerProcess
from producer import ProducerVideoHandlerProcess
from sm import SharedMemoryHandler
from torch.multiprocessing import set_start_method, get_start_method, freeze_support
import multiprocessing as mp
import traceback
import sys, os


shared_mem_handler = SharedMemoryHandler()
shared_memory_object_tuple = shared_mem_handler.create_shared_memory()

if __name__ == '__main__':
    try:

        ## Creating shared memory handler to hold the image frames
        #ctx = mp.get_context('spawn')
        #global shared_memory_object_tuple
        # mp.set_executable(os.path.join(sys.exec_prefix, 'pythonw.exe'))
        set_start_method('spawn', force=True)

        ### Calling Producer class process

        producer_reader_process = ProducerVideoHandlerProcess(shared_memory_object_tuple)
        producer_reader_process.start()

        ### Calling Consumer class process
        consumer_reader_process = ConsumerVideoHandlerProcess(shared_memory_object_tuple)
        consumer_reader_process.start()

        producer_reader_process.join()
        consumer_reader_process.join()

        ## Cleanup

        producer_reader_process.terminate()
        del producer_reader_process
        consumer_reader_process.terminate()
        del consumer_reader_process
        del shared_memory_object_tuple
        print("Main Process completed successfully")
    except (ValueError, Exception):
        print("Error in Main function : %s", traceback.format_exc())

#if __name__ == '__main__':
    #set_start_method('spawn')
#    main()

I also moved entire code under if __name__ == '__main__' to a separate function main() and just called the main() method by setting the set_start_method('spawn') from if __name__ == '__main__'.

if __name__ == '__main__':
    set_start_method('spawn')
    main()

But this also throwing the same error. And also tried the below lines of code by getting the context and this also did not work.

ctx = mp.get_context('spawn')
producer_reader_process = ctx.Process(target=ProducerVideoHandlerProcess, args=(shared_memory_object_tuple,))
producer_reader_process.start()
consumer_reader_process = ctx.Process(target=ConsumerVideoHandlerProcess, args=(shared_memory_object_tuple,))
consumer_reader_process.start()

Could you let me know how can i fix this issue. Thank you in advance.

Update 1

I changed the code to below as per suggestions from @CharchitAgarwal.

from consumer import ConsumerVideoHandlerProcess
from producer import ProducerVideoHandlerProcess
from sm import SharedMemoryHandler
from multiprocessing import set_start_method, get_start_method, freeze_support
import traceback

if __name__ == '__main__':
    #freeze_support()
    set_start_method('spawn')
    print('Start method already set: ', get_start_method())

shared_mem_handler = SharedMemoryHandler()
shared_memory_object_tuple = shared_mem_handler.create_shared_memory()

try:
        producer_reader_process = ProducerVideoHandlerProcess(shared_memory_object_tuple)
        producer_reader_process.start()

        consumer_reader_process = ConsumerVideoHandlerProcess(shared_memory_object_tuple)
        consumer_reader_process.start()

        producer_reader_process.join()
        consumer_reader_process.join()

        ## Cleanup

        producer_reader_process.terminate()
        del producer_reader_process
        consumer_reader_process.terminate()
        del consumer_reader_process
        del shared_memory_object_tuple

        print("Main Process completed successfully")

except (ValueError, Exception):
        print("Error in Main function : %s", traceback.format_exc())

But the shared memory is not getting updated properly. It is always showing a numpy array with all zeros. The producer class is generating a numpy array with non zero values and is placing in the shared memory. But the consumer class is getting all zeros when it is accessing the shared memory.

And at the end getting the below error.

Main Process completed successfully
(conda-pv-pytorch-2) ubuntu@ip-172-31-22-56:~/multi-process-testing$ /home/ubuntu/anaconda3/envs/conda-pv-pytorch-2/lib/python3.9/multiprocessing/resource_tracker.py:216: UserWarning: resource_tracker: There appear to be 2 leaked semaphore objects to clean up at shutdown
  warnings.warn('resource_tracker: There appear to be %d '

Upate 2

Tried different options to see how to fix this issue. Here is the minimalistic code that we can use to reproduce. Appreciate if someone can suggest a solution.

from multiprocessing import Process, Lock, set_start_method, Array
import numpy as np
import random
import time

def create_a_dummy_frame():
    count = random.randint(1, 50)
    shape = [1, 4]
    frame = (np.ones(shape) * count)
    return frame

def modify_array_1(sharedarray, a_buffer_arrray, shape):
        ### Acquire the lock in the 1st  process
        sharedarray.acquire()
        print("Address in the 1st process : ", id(sharedarray))
        image_frame = create_a_dummy_frame()
        np.copyto(a_buffer_arrray, image_frame)

        print("a --- {}".format(a_buffer_arrray))

def modify_array_2(sharedarray, b_buffer_arrray, shape):
        time.sleep(1)
        print("Address in the 2nd process : ", id(sharedarray))
        b_buffered_array = b_buffer_arrray.astype("uint8").copy()
        print("b --- {}".format(b_buffered_array))
        ### Release the lock in the 2nd process
        sharedarray.release()

if __name__ == "__main__":
    set_start_method('spawn')  ### spwan start method.
    #set_start_method('fork')  ### default fork method in unix.
    lock = Lock()
    image_frame_shape = [1, 4]
    shared_memory_array = Array('d', 4, lock=lock)
    buffered_array = np.frombuffer(shared_memory_array.get_obj(), dtype='d').reshape(image_frame_shape)

    p1 = Process(target=modify_array_1, args=(shared_memory_array, buffered_array, image_frame_shape))
    p1.start()

    p2 = Process(target=modify_array_2, args=(shared_memory_array, buffered_array, image_frame_shape))
    p2.start()

    p1.join()
    p1.join()
    #print(list(shared_memory_array))
    #print(list(buffered_array))

In the main method, if i use set_start_method('spawn'), the output is as below.

(conda-pv-pytorch-2) ubuntu@ip-11-22-33-44:~/multi-process-testing$ python3 test1.py
Address in the 1st process :  140169076553664
a --- [[9. 9. 9. 9.]]
Address in the 2nd process :  140534970750912
b --- [[0 0 0 0]]

Clearly two processes are not using the same shared array.

If I use the default fork start method, i can see the same output.

(conda-pv-pytorch-2) ubuntu@ip-11-22-33-44:~/multi-process-testing$ python3 test1.py
Address in the 1st process :  140170642829904
a --- [[22. 22. 22. 22.]]
Address in the 2nd process :  140170642829904
b --- [[22 22 22 22]]

Here the address for the shared array is same in both the processes.

How can i make sure the array variable can be accessed in both the processes when i use the spawn method.

I tried the suggestion from this post as well by creating a context object, but that also did not help.

context = get_context('spawn')
    lock = context.Lock()
    image_frame_shape = [1, 4]
    shared_memory_array = context.Array('d', 4, lock=lock)
    buffered_array = np.frombuffer(shared_memory_array.get_obj(), dtype='d').reshape(image_frame_shape)

    p1 = context.Process(target=modify_array_1, args=(shared_memory_array, buffered_array, image_frame_shape))
    p1.start()

    p2 = context.Process(target=modify_array_2, args=(shared_memory_array, buffered_array, image_frame_shape))
    p2.start()

Appreciate your help.

0 Answers
Related