Sửa lỗi 'TypeError: cannot pickle' trong Python Multiprocessing

intermediate🐍 Python2026-05-26| Python 3.x, Linux/macOS/Windows, module multiprocessing

Error Message

TypeError: cannot pickle '<lambda>' object
#python#multiprocessing#pickle#song-song#dong-thoi

TL;DR: Cách khắc phục nhanh

Python ném lỗi TypeError: cannot pickle '<lambda>' object vì module multiprocessing cần "đóng gói" (tuần tự hóa - serialize) mã của bạn để gửi đến các nhân CPU khác. Thư viện pickle tiêu chuẩn đơn giản là không biết cách xử lý các hàm ẩn danh như lambda hoặc các logic lồng nhau.

Giải pháp: Di chuyển logic của bạn ra khỏi lambda và đưa vào một hàm có tên ở cấp cao nhất (top level) trong script của bạn. Điều này cung cấp cho Python một "bản đồ" rõ ràng để tìm hàm trong một tiến trình mới.

import multiprocessing

# ❌ CÁCH NÀY THẤT BẠI
# with multiprocessing.Pool(4) as p:
#     p.map(lambda x: x * 10, [1, 2, 3])

# ✅ CÁCH NÀY THÀNH CÔNG
def multiply_by_ten(x):
    return x * 10

if __name__ == "__main__":
    with multiprocessing.Pool(processes=4) as p:
        print(p.map(multiply_by_ten, [1, 2, 3]))

Tại sao tuần tự hóa thất bại

Khi bạn bắt đầu một Pool, Python sẽ tạo ra các tiến trình worker mới. Các worker này không chia sẻ bộ nhớ với script chính của bạn. Để gửi hàm của bạn đến chúng, Python sẽ "pickle" nó—chuyển đổi hàm thành một luồng byte có thể được xây dựng lại ở phía bên kia.

Pickle hoạt động bằng cách tra cứu các đối tượng theo tên cụ thể của chúng. Vì lambda là hàm ẩn danh, nó không có tên duy nhất để tiến trình worker có thể import. Bạn sẽ gặp phải rào cản tương tự với:

- **Hàm lồng nhau (Nested functions):** Các hàm được định nghĩa bên trong một hàm khác.
- **Phương thức instance (Instance methods):** Các phương thức gắn liền với một class (đặc biệt là trên Windows/macOS).
- **Tài nguyên hệ thống:** Các handle file đang mở, kết nối cơ sở dữ liệu hoặc socket mạng.

Sự khác biệt giữa các hệ điều hành cũng rất quan trọng. Linux thường mặc định sử dụng fork, giúp sao chép toàn bộ không gian bộ nhớ và có thể cho phép bạn sử dụng các đối tượng không thể pickle. Tuy nhiên, macOS (từ Python 3.8) và Windows sử dụng spawn, bắt đầu từ một trạng thái mới hoàn toàn và thực thi các quy tắc pickling nghiêm ngặt.

Ba cách để khắc phục

1. Sử dụng hàm Top-Level

Đây là cách tiếp cận sạch nhất. Bằng cách đặt hàm worker của bạn ở cấp module, bạn đảm bảo rằng mọi tiến trình worker đều có thể tìm thấy và import nó theo tên. Cách này đáng tin cậy, dễ đọc và không yêu cầu thêm thư viện.

# logic.py
def heavy_lifting(data):
    return sum(data) / len(data)

# main.py
from multiprocessing import Pool
from logic import heavy_lifting

if __name__ == "__main__":
    items = [[1, 2], [3, 4], [5, 6]]
    with Pool() as pool:
        results = pool.map(heavy_lifting, items)

2. Thay thế Pickle bằng Pathos

Nếu dự án của bạn phụ thuộc nhiều vào lambda hoặc cấu trúc class phức tạp, hãy thử thư viện pathos. Nó sử dụng dill thay vì pickle. dill thông minh hơn nhiều; nó có thể tuần tự hóa hầu hết mọi thứ, bao gồm cả closure và lambda, bằng cách nắm bắt bytecode thực tế.

Cài đặt qua terminal:

pip install pathos

Sau đó, thay đổi phần import của bạn:

from pathos.multiprocessing import ProcessingPool as Pool

if __name__ == "__main__":
    # Cách này hoạt động hoàn hảo với pathos
    with Pool(4) as p:
        result = p.map(lambda x: x ** 2, [10, 20, 30])
        print(result)

3. Chuyển đổi phương thức thành Static Method

Việc cố gắng pickle self.my_method thường thất bại vì Python cố gắng pickle toàn bộ instance của đối tượng (self). Nếu đối tượng của bạn chứa một kết nối cơ sở dữ liệu hoặc cache dữ liệu 500MB, tiến trình sẽ bị crash. Thay vào đó, hãy sử dụng @staticmethod để tách biệt logic khỏi trạng thái của instance.

class DataProcessor:
    @staticmethod
    def clean_string(text):
        return text.strip().lower()

    def run(self, data_list):
        with multiprocessing.Pool() as p:
            # Chúng ta truyền static method, tránh bẫy pickling 'self'
            return p.map(self.clean_string, data_list)

Cách xác minh giải pháp của bạn

Đừng giả định rằng nó hoạt động chỉ vì nó chạy được trên máy Linux cục bộ của bạn. Hãy thực hiện ba bước sau để đảm bảo tính di động:

- **Kiểm tra khả năng tương thích với 'spawn':** Thêm `multiprocessing.set_start_method('spawn', force=True)` ở ngay đầu script. Nếu nó hoạt động ở đây, nó sẽ hoạt động trên mọi hệ điều hành.
- **Guard `__main__`:** Luôn bọc điểm bắt đầu của bạn trong `if __name__ == "__main__":`. Không có điều này, các worker có thể cố gắng tạo ra worker của riêng chúng, dẫn đến vòng lặp đệ quy gây quá tải CPU.
- **Chạy thử với lô nhỏ:** Chạy thử với 2 worker và 10 item. Nếu có sự không khớp trong tuần tự hóa, Python sẽ báo cáo trong vài giây đầu tiên.

Đọc thêm

- [Tài liệu Python: Các đối tượng có thể Pickle](https://docs.python.org/3/library/pickle.html#what-can-be-pickled-and-unpickled)
- [Hiểu về Spawn và Fork](https://docs.python.org/3/library/multiprocessing.html#contexts-and-start-methods)

Related Error Notes