Các lỗi phổ biến trong hàng đợi tác vụ đa luồng Python và cách khắc phục

Khi phát triển hàng đợi tác vụ đa luồng bằng Python, bạn thường sẽ gặp phải nhiều loại lỗi khác nhau. Bài viết này dựa trên các tình huống thực tế để phân tích ba lỗi phổ biến và cung cấp giải pháp chi tiết. Các bạn quan tâm có thể tham khảo thêm.

1. Giới thiệu

Khi sử dụng Python để phát triển hàng đợi tác vụ đa luồng, thường sẽ gặp phải các lỗi như nhập vòng lặp, lỗi cách truy cập đối tượng, hoặc phạm vi biến. Bài viết này phân tích ba lỗi tiêu biểu dựa trên tình huống thực tế và đưa ra giải pháp cụ thể. Các tình huống được đề cập gồm:

  • Ứng dụng Flask + SQLAlchemy

  • Hàng đợi tác vụ đa luồng (queue.Queue + threading.Thread)

  • Xử lý đối chiếu dữ liệu từ cơ sở dữ liệu

2. Vấn đề 1: Nhập vòng lặp (Circular Import)

Phân tích lỗi

Thông báo lỗi:

pgsql
ImportError: cannot import name ‘start_processing’ from partially initialized module ‘task.national_match_task’ (most likely due to a circular import)
 
Nguyên nhân:
  • app.py nhập start_processing từ national_match_task.py

  • Trong khi national_match_task.py lại nhập app từ app.py

  • Điều này khiến Python không thể khởi tạo mô-đun một cách chính xác

Giải pháp

Phương pháp 1: Nhập trễ

Nhập phụ thuộc bên trong hàm thay vì ở đầu tệp:

# national_match_task.py
def get_failed_records():
from app import app # Nhập trễ
with app.app_context():
records = db.session.query(CustomerOrder).filter(…).all()
return records

Phương pháp 2: Tiêm phụ thuộc

Để start_processing nhận tham số app thay vì nhập trực tiếp:

# national_match_task.py
def start_processing(app):  # Nhận tham số app
    # Sử dụng app thay vì nhập trực tiếp
 
# app.py
from task.national_match_task import start_processing
start_processing(app)  # Truyền đối tượng app
 
Phương pháp 3: Sử dụng flask.current_app
from flask import current_app as app # Thay vì nhập trực tiếp
 

3. Vấn đề 2: Không thể sử dụng chỉ mục với đối tượng SQLAlchemy (‘CustomerOrder’ object is not subscriptable)

Phân tích lỗi

Thông báo lỗi:

TypeError: ‘CustomerOrder’ object is not subscriptable

Nguyên nhân:

  • Hàm match_nationwide_numbers() mong nhận được một dict, nhưng thực tế truyền vào là đối tượng SQLAlchemy

  • Cố gắng truy cập bằng item['prefix'] nhưng SQLAlchemy phải truy cập bằng item.prefix

Giải pháp

Giải pháp 1: Sửa hàm khớp để sử dụng thuộc tính

# match_phone_number.py
def match_nationwide_numbers(item, cookie, logger):
if not (item.prefix and item.suffix): # Sử dụng . để truy cập thuộc tính
logger.warning(“Thiếu thông tin tiền tố hoặc hậu tố cần thiết”)
return {“匹配状态”: “Thất bại: Thiếu tiền tố hoặc hậu tố”}
# Logic khác…

Giải pháp 2: Chuyển đối tượng sang dict trước khi gọi

# national_match_task.py
def worker():
item = queue.get()
item_dict = {
‘prefix’: item.prefix,
‘suffix’: item.suffix,
‘tracking_number’: item.tracking_number,
}
result = match_nationwide_numbers(item_dict, item.cookie, logger)

Giải pháp 3: Thêm cơ chế thử lại

max_retries = 3
retry_count = getattr(item, ‘_retry_count’, 0)
if retry_count < max_retries:
item._retry_count = retry_count + 1
queue.put(item) # Đưa lại vào hàng đợi
 

Phân tích lỗi tiếp theo

Thông báo lỗi:

UnboundLocalError: cannot access local variable ‘item’ where it is not associated with a value

Nguyên nhân:

  • Biến item chưa được khởi tạo bên ngoài khối try

  • Khi queue.get() xảy ra lỗi, item không có giá trị nhưng khối finally vẫn cố truy cập

Giải pháp

Giải pháp 1: Khởi tạo biến item

def worker():
item = None # Khởi tạo
try:
item = queue.get(timeout=1)
# Xử lý logic…
except queue.Empty:
continue
finally:
if item is not None: # Đảm bảo biến đã được gán
queue.task_done()

Giải pháp 2: Kiểm tra biến có tồn tại

finally:
if ‘item’ in locals() and item is not None:
queue.task_done()

Giải pháp 3: Tái cấu trúc để giảm nhầm lẫn phạm vi biến

def worker():
    while True:
        process_next_item()
 
def process_next_item():
    item = queue.get(timeout=1)
    try:
        # Xử lý logic…
    finally:
        queue.task_done()

5. Tổng kết và thực tiễn tốt

  1. Tránh nhập vòng lặp

    • Sử dụng tiêm phụ thuộc hoặc nhập trễ

    • Tránh các mô-đun phụ thuộc lẫn nhau

  2. Xử lý đối tượng SQLAlchemy đúng cách

    • Dùng . để truy cập thuộc tính thay vì []

    • Chuyển thành dict khi cần thiết

  3. Xử lý hàng đợi đa luồng an toàn

    • Khởi tạo biến để tránh lỗi UnboundLocalError

    • Thêm cơ chế retry để tránh lặp vô hạn

    • Dùng finally để đảm bảo tài nguyên được giải phóng

6. Ví dụ mã hoàn chỉnh đã sửa

import threading
import queue
import time
from flask import current_app as app
from models import CustomerOrder
 
def worker():
    item = None  # khởi tạo
    try:
        item = queue.get(timeout=1)
        if item is None:
            return
 
        logger.info(f”Xử lý hồ sơ: {item.tracking_number}”)
        result = match_nationwide_numbers({
            ‘prefix’: item.prefix,
            ‘suffix’: item.suffix,
        }, item.cookie, logger)
 
        update_record(item.id, result[“Trạng thái phù hợp”], result.get(“Số điện thoại”))
        
    except queue.Empty:
        return
    except Exception as e:
        logger.error(f”Xử lý lỗi: {e}”)
        if item and getattr(item, ‘_retry_count’, 0) < 3:
            item._retry_count += 1
            queue.put(item)
    finally:
        if item is not None:
            queue.task_done()
 
def start_processing(app):
    for _ in range(5):
        threading.Thread(target=worker, daemon=True).start()

Kết luận

Hàng đợi tác vụ đa luồng trong Python rất hữu ích nhưng cũng dễ gặp các tình huống phức tạp. Bằng cách thiết kế cấu trúc hợp lý, khởi tạo biến đúng cách, và xử lý đúng cách với các đối tượng, bạn có thể giảm thiểu tối đa lỗi. Hy vọng bài viết này sẽ giúp bạn phát triển ứng dụng đa luồng với Python một cách ổn định hơn!

 

📌 Bài viết này được đóng góp bởi người dùng và bản quyền thuộc về người dùng đã xây dựng bài viết. Bản quyền thuộc về tác giả gốc và chỉ dùng cho mục đích học tập và giao tiếp. Nếu có bất kỳ vi phạm nào, vui lòng liên hệ với chúng tôi để xóa nó.

Để lại một bình luận

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *