Почему возникает ошибка «cannot pickle '_tkinter.tkapp' object» при использовании модуля Multiprocessing?

Здравствуйте! Я разрабатываю программу с использованием графического модуля tkinter и столкнулся с проблемой: не удается передать виджеты в функции, которые запускаются в параллельном процессе с помощью класса Process. При этом возникает ошибка "cannot pickle '_tkinter.tkapp' object". Тот же код без проблем работает в библиотеке threading, однако в ней нельзя принудительно остановить непрерывный процесс. Мне непонятно, почему в Thread все функционирует корректно, а в Process - нет. <br/> Используемая версия: python 3.11.9 64bit Windows <br/> Версия tkinter Tcl/Tk 8.6.12 <br/> <br/> Пример кода: <pre><code class="python">import time
import multiprocessing
from threading import Thread
from tkinter import *
from tkinter import ttk

# Модуль Multiprocessing и Thread, пока работает только так...
# Функция контроля работы потока расчета
def live(t1,w):
    while t1.is_alive():
        print("Ok")
        time.sleep(0.2)
    else:
        w.destroy()
# Функция счета
def s4et(k):
    i=0
    while i&lt;10**6:
        i+=1
        d=i*k
        print(d)
# Функция открытия окна при расчете
def pusk():
    # Функция закрытия окна по нажатию кнопки
    def denide():
        t1.terminate()
        window.destroy()    # Закрытие окна
    # Новое вспомогательное окно
    window=Toplevel()
    window.geometry(f"{int(wtk/2)}x{int(htk/2)}+{int(w_wtk+wtk/4)}+{int(w_htk+htk/4)}")
    
    btn1=Button(master=window,text="Stop",cursor="hand2",border=4,command=denide,activebackground="white",activeforeground="black",bd=5,font=("Times","12"))
    btn1.pack(anchor='sw',pady=5,padx=0)
    t1=multiprocessing.Process(target=s4et,args=(2,),daemon=True) # Поток
    t2=Thread(target=live,args=(t1,window),daemon=True) # работает
    t3=multiprocessing.Process(target=live,args=(t1,w
  • 5 декабря 2024 г. 18:23
Ответы на вопрос 2
Ошибка "cannot pickle '_tkinter.tkapp' object", с которой вы столкнулись, происходит из-за того, что объекты tkinter не могут быть сериализованы (преобразованы в байтовый поток) для передачи между процессами при использовании модуля `multiprocessing`. Каждый процесс в `multiprocessing` имеет свое собственное пространство памяти, и объекты tkinter (например, окна, виджеты) существуют в единственном процессе, где они были созданы.

Когда вы хотите передать виджет (например, `window`), в другую функцию, выполняемую в другом процессе, это вызывает ошибку, потому что `multiprocessing` не может сериализовать объект tkinter.

В отличие от `threading`, где потоки работают в одном и том же пространстве памяти и могут напрямую работать с объектами tkinter, `multiprocessing` создаёт новые процессы с их собственным пространством памяти и требует сериализации для передачи данных между ними.

Для решения проблемы вы можете рассмотреть следующие варианты:

1. **Использовать `Queue` или другие механизмы передачи данных:** Вместо передачи виджетов, используйте `Queue` для передачи сообщений между процессом и главным потоком, чтобы управлять состоянием приложения.

2. **Обработка задач в главном потоке:** Можно избежать использования `multiprocessing` и использовать `threading` для продолжительной работы, в том числе, добавляя логику для завершения потоков по сигналу от виджета.

Вот пример, как вы можете использовать `Queue` для взаимодействия между процессами:

```python
import time
import multiprocessing
from tkinter import *
from tkinter import ttk

def s4et(k, q):
    """Функция счета."""
    i = 0
    while i < 10 ** 6:
        i += 1
        d = i * k
        q.put(d)  # Отправляем результат в очередь

def check_for_results(q, window):
    """Проверка результатов выполнения."""
    while True:
        try:
            while not q.empty():
                result = q.get()
                print(result)
        except Exception as e:
            print(f"Error: {e}")
        
        time.sleep(0.2)
        if not window.winfo_exists():
            break  # Закрываем цикл, если окно уже закрыто

def pusk():
    def denide():
        t1.terminate()  # Завершение процесса
        window.destroy()  # Закрытие окна

    window = Toplevel()
    window.geometry("400x200")
    
    btn1 = Button(master=window, text="Stop", command=denide)
    btn1.pack(pady=20)
    
    q = multiprocessing.Queue()  # Создаем очередь для передачи данных
    t1 = multiprocessing.Process(target=s4et, args=(2, q), daemon=True)
    t1.start()  # Стартуем процесс

    t2 = Thread(target=check_for_results, args=(q, window), daemon=True)
    t2.start()  # Запускаем поток для проверки результатов

# Запуск главного окна
root = Tk()
root.geometry("200x100")

start_button = Button(root, text="Start", command=pusk)
start_button.pack(pady=20)

root.mainloop()
```

В этом примере используется очередь `multiprocessing.Queue` для хранения результатов функции `s4et`, а другой поток проверяет очередь на наличие данных. Это позволяет избежать передачи объектов tkinter между процессами и сохраняет возможность обработки данных асинхронно.
Дебильно, но работает 
Похожие вопросы