Странный баг с многопроцессностью

Я изучаю многопроцессность, многопоточность и асинхронность в питоне. Я написал вот такой простой код для практики:

import time
import tkinter as tk
import multiprocessing as mp

def start():
    process = mp.Process(target=lambda: time.sleep(2))
    
    print("begin")
    
    process.run()
    
    print("end")

def main():
    root = tk.Tk()
    
    tk.Button(root, text="Button", command=start).pack()
    
    tk.mainloop()

if __name__ == "__main__":
    main()

Но я получаю вот такую странную ошибку:

begin
Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Users\egork\AppData\Local\Programs\Python\Python311\Lib\tkinter\__init__.py", line 1948, in __call__
    return self.func(*args)
           ^^^^^^^^^^^^^^^^
  File "c:\Users\egork\OneDrive\\u0420\u0430\u0431\u043e\u0447\u0438\u0439 \u0441\u0442\u043e\u043b\\u041f\u0440\u043e\u0435\u043a\u0442\u044b Python\testing\index.py", line 10, in start
    process.start()
  File "C:\Users\egork\AppData\Local\Programs\Python\Python311\Lib\multiprocessing\process.py", line 121, in start
    self._popen = self._Popen(self)
                  ^^^^^^^^^^^^^^^^^
  File "C:\Users\egork\AppData\Local\Programs\Python\Python311\Lib\multiprocessing\context.py", line 224, in _Popen
    return _default_context.get_context().Process._Popen(process_obj)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\egork\AppData\Local\Programs\Python\Python311\Lib\multiprocessing\context.py", line 336, in _Popen
    return Popen(process_obj)
           ^^^^^^^^^^^^^^^^^^
  File "C:\Users\egork\AppData\Local\Programs\Python\Python311\Lib\multiprocessing\popen_spawn_win32.py", line 95, in __init__
    reduction.dump(process_obj, to_child)
  File "C:\Users\egork\AppData\Local\Programs\Python\Python311\Lib\multiprocessing\reduction.py", line 60, in dump
    ForkingPickler(file, protocol).dump(obj)
AttributeError: Can't pickle local object 'start.<locals>.<lambda>'
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "C:\Users\egork\AppData\Local\Programs\Python\Python311\Lib\multiprocessing\spawn.py", line 122, in spawn_main
    exitcode = _main(fd, parent_sentinel)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\egork\AppData\Local\Programs\Python\Python311\Lib\multiprocessing\spawn.py", line 132, in _main
    self = reduction.pickle.load(from_parent)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
EOFError: Ran out of input

Но когда я меняю лямбду-функцию паузы в аргументе target процесса на обычную функцию (то есть def sleep(): time.sleep(2) и process = mp.Process(target=sleep)) то код работает как надо. Если многопроцессность изменить на многопоточность то все будет работать правильно и с обычной функцией, и с лямбдой.

Пожалуйста, скажите почему так происходит и как это решить если я использую многопроцессность и в лямбду как в коде.

Версия питона 3.11.6


Ответы (3 шт):

Автор решения: Bestia

По-моему, в ошибке все сказано.

AttributeError: Can't pickle local object 'start.<locals>.<lambda>'

Не может он с лямбдой работать

Вместо этого используй:

process = mp.Process(target=time.sleep, target=(2,))

Помни, что ты можешь там вызывать функцию из модулей напрямую, а не создавать свою

→ Ссылка
Автор решения: Egork.vorobiev

Вот так работает:

process = mp.Process(target=time.sleep, args=(2,))
→ Ссылка
Автор решения: eri

Process в Виндовс использует метод spawn. Этот метод запускает питон без контекста и импортирует в него функцию таргет. Импортировать можно только глобальную функцию. С лямбдами, локальными функциями и методами классов это работать не может.

В линукс используется метод fork который делает копию процесса и в этом случае работают лямбды.

→ Ссылка