PYTHON CONCURRENCY
CÁC CHỦ ĐỀ
BÀI MỚI NHẤT
MỚI CẬP NHẬT

Thông báo: Download 4 khóa học Python từ cơ bản đến nâng cao tại đây.

Tìm hiểu về điều kiện race của threading Lock trong Python

Trong lập trình đa luồng, việc nhiều luồng cùng truy cập và thay đổi một biến chia sẻ có thể dẫn đến các kết quả không mong muốn và không thể đoán trước, gây ra hiện tượng gọi là "điều kiện race". Đây là một vấn đề phổ biến và tiềm ẩn nhiều nguy cơ trong các ứng dụng đòi hỏi tính đồng bộ và chính xác cao. Để giải quyết vấn đề này, Python cung cấp một công cụ hữu ích là threading.Lock. Bài viết này sẽ giúp bạn hiểu rõ hơn về điều kiện race, cách sử dụng đối tượng Lock để ngăn chặn chúng, và minh họa bằng các ví dụ cụ thể để bạn có thể áp dụng vào thực tế lập trình của mình.

test php

banquyen png
Bài viết này được đăng tại freetuts.net, không được copy dưới mọi hình thức.

Điều kiện race là gì?

Điều kiện race xảy ra khi hai hoặc nhiều luồng cùng truy cập một biến chia sẻ đồng thời, dẫn đến kết quả không thể đoán trước.

Trong tình huống này, luồng đầu tiên đọc giá trị từ biến chia sẻ. Cùng lúc đó, luồng thứ hai cũng đọc giá trị từ biến chia sẻ đó.

Sau đó, cả hai luồng cố gắng thay đổi giá trị của biến chia sẻ. Vì các cập nhật xảy ra đồng thời, nên sẽ tạo ra một cuộc "đua" để xác định thay đổi của luồng nào sẽ được lưu lại.

Bài viết này được đăng tại [free tuts .net]

Giá trị cuối cùng của biến chia sẻ phụ thuộc vào luồng nào hoàn thành việc cập nhật cuối cùng. Luồng nào thay đổi giá trị cuối cùng sẽ "thắng cuộc đua".

Ví dụ về điều kiện race

Ví dụ sau minh họa một điều kiện race:

from threading import Thread
from time import sleep

counter = 0

def increase(by):
    global counter

    local_counter = counter
    local_counter += by

    sleep(0.1)

    counter = local_counter
    print(f'counter={counter}')

# tạo các luồng
t1 = Thread(target=increase, args=(10,))
t2 = Thread(target=increase, args=(20,))

# bắt đầu các luồng
t1.start()
t2.start()

# đợi các luồng hoàn thành
t1.join()
t2.join()

print(f'Giá trị cuối cùng của counter là {counter}')

Trong chương trình này, cả hai luồng cố gắng thay đổi giá trị của biến counter cùng lúc. Giá trị của biến counter phụ thuộc vào luồng nào hoàn thành cuối cùng.

Nếu luồng t1 hoàn thành trước luồng t2, bạn sẽ thấy kết quả sau:

counter=10
counter=20
Giá trị cuối cùng của counter là 20

Ngược lại, bạn sẽ thấy kết quả sau:

counter=20
counter=10
Giá trị cuối cùng của counter là 10

Sử dụng threading lock để ngăn chặn điều kiện race trong Python

Để ngăn chặn điều kiện race, bạn có thể sử dụng một khóa luồng (threading lock).

Khóa luồng là một nguyên thủy đồng bộ cung cấp quyền truy cập độc quyền vào tài nguyên chia sẻ trong một ứng dụng đa luồng. Khóa luồng cũng được gọi là mutex, viết tắt của "mutual exclusion" (loại trừ lẫn nhau).

Thông thường, một khóa luồng có hai trạng thái: khóa và mở khóa. Khi một luồng chiếm khóa, khóa chuyển sang trạng thái khóa. Luồng này có thể truy cập độc quyền vào tài nguyên chia sẻ.

Các luồng khác cố gắng chiếm khóa trong khi khóa đang bị khóa sẽ bị chặn và phải đợi cho đến khi khóa được giải phóng.

Trong Python, bạn có thể sử dụng lớp Lock từ mô-đun threading để tạo một đối tượng khóa:

Đầu tiên, tạo một thể hiện của lớp Lock:

lock = Lock()

Mặc định, khóa được mở khóa cho đến khi một luồng chiếm nó.

Thứ hai, chiếm khóa bằng cách gọi phương thức acquire():

lock.acquire()

Thứ ba, giải phóng khóa khi luồng hoàn thành việc thay đổi biến chia sẻ:

lock.release()

Ví dụ sau cho thấy cách sử dụng đối tượng Lock để ngăn chặn điều kiện race trong chương trình trước:

from threading import Thread, Lock
from time import sleep

counter = 0

def increase(by, lock):
    global counter

    lock.acquire()

    local_counter = counter
    local_counter += by

    sleep(0.1)

    counter = local_counter
    print(f'counter={counter}')

    lock.release()

lock = Lock()

# tạo các luồng
t1 = Thread(target=increase, args=(10, lock))
t2 = Thread(target=increase, args=(20, lock))

# bắt đầu các luồng
t1.start()
t2.start()

# đợi các luồng hoàn thành
t1.join()
t2.join()

print(f'Giá trị cuối cùng của counter là {counter}')

Kết quả đầu ra:

counter=10
counter=30
Giá trị cuối cùng của counter là 30

Sử dụng khóa luồng với câu lệnh with trong Python

Dễ dàng hơn để sử dụng đối tượng khóa với câu lệnh with để chiếm và giải phóng khóa trong một khối mã:

import threading

# Tạo đối tượng khóa
lock = threading.Lock()

# Thực hiện một số thao tác trong vùng quan trọng
with lock:
    # Khóa đã được chiếm trong khối with
    # Thực hiện các thao tác trên tài nguyên chia sẻ
    # ...

# khóa được giải phóng ngoài khối with

Ví dụ, bạn có thể sử dụng câu lệnh with mà không cần gọi các phương thức acquire()release() trong ví dụ trên như sau:

from threading import Thread, Lock
from time import sleep

counter = 0

def increase(by, lock):
    global counter

    with lock:
        local_counter = counter
        local_counter += by

        sleep(0.1)

        counter = local_counter
        print(f'counter={counter}')

lock = Lock()

# tạo các luồng
t1 = Thread(target=increase, args=(10, lock))
t2 = Thread(target=increase, args=(20, lock))

# bắt đầu các luồng
t1.start()
t2.start()

# đợi các luồng hoàn thành
t1.join()
t2.join()

print(f'Giá trị cuối cùng của counter là {counter}')

Định nghĩa lớp Counter an toàn với luồng sử dụng đối tượng Lock trong Python

Ví dụ sau minh họa cách định nghĩa một lớp Counter an toàn với luồng sử dụng đối tượng Lock:

from threading import Thread, Lock
from time import sleep

class Counter:
    def __init__(self):
        self.value = 0
        self.lock = Lock()

    def increase(self, by):
        with self.lock:
            current_value = self.value
            current_value += by

            sleep(0.1)

            self.value = current_value
            print(f'counter={self.value}')

def main():
    counter = Counter()
    # tạo các luồng
    t1 = Thread(target=counter.increase, args=(10,))
    t2 = Thread(target=counter.increase, args=(20,))

    # bắt đầu các luồng
    t1.start()
    t2.start()

    # đợi các luồng hoàn thành
    t1.join()
    t2.join()

    print(f'Giá trị cuối cùng của counter là {counter.value}')

if __name__ == '__main__':
    main()

Kết bài

Điều kiện race là một vấn đề thường gặp khi nhiều luồng cùng truy cập và thay đổi một biến chia sẻ đồng thời. Để ngăn chặn điều này, việc sử dụng đối tượng khóa luồng (threading.Lock) là rất cần thiết. Bằng cách sử dụng phương thức acquire() để chiếm khóa và release() để giải phóng khóa, ta có thể đảm bảo rằng chỉ có một luồng được truy cập và thay đổi biến chia sẻ tại một thời điểm. Hơn nữa, sử dụng đối tượng khóa với câu lệnh with giúp việc quản lý khóa trở nên đơn giản và hiệu quả hơn. Nhờ đó, bạn có thể viết các chương trình đa luồng an toàn và chính xác, giảm thiểu rủi ro và tăng hiệu suất của ứng dụng.

Cùng chuyên mục:

Cách thêm Progress Bar trong Python với chỉ một dòng Code

Cách thêm Progress Bar trong Python với chỉ một dòng Code

Toán tử Walrus Operator- Tính năng mới trong Python 3.8

Toán tử Walrus Operator- Tính năng mới trong Python 3.8

Cách nạp dữ liệu Machine Learning từ File trong Python

Cách nạp dữ liệu Machine Learning từ File trong Python

Hướng dẫn sử dụng Google Sheets API với Python

Hướng dẫn sử dụng Google Sheets API với Python

Xây dựng  web Python tự động hóa Twitter | Flask, Heroku, Twitter API & Google Sheets API

Xây dựng web Python tự động hóa Twitter | Flask, Heroku, Twitter API & Google Sheets API

Xây dựng Web Machine Learning đẹp mắt với Streamlit và Scikit-learn trong Python

Xây dựng Web Machine Learning đẹp mắt với Streamlit và Scikit-learn trong Python

Hướng dẫn tạo Chatbot đơn giản bằng PyTorch

Hướng dẫn tạo Chatbot đơn giản bằng PyTorch

11 mẹo và thủ thuật để viết Code Python hiệu quả hơn

11 mẹo và thủ thuật để viết Code Python hiệu quả hơn

Hướng dẫn làm ứng dụng TODO với Flask dành cho người mới bắt đầu trong Python

Hướng dẫn làm ứng dụng TODO với Flask dành cho người mới bắt đầu trong Python

Hướng dẫn viết Snake Game bằng Python

Hướng dẫn viết Snake Game bằng Python

Cách sử dụng chế độ interactive trong Python

Cách sử dụng chế độ interactive trong Python

Cách sử dụng Python Debugger với hàm breakpoint()

Cách sử dụng Python Debugger với hàm breakpoint()

Xây dựng ứng dụng Web Style Transfer với PyTorch và Streamlit

Xây dựng ứng dụng Web Style Transfer với PyTorch và Streamlit

Cách cài đặt Jupyter Notebook trong môi trường Conda và thêm Kernel

Cách cài đặt Jupyter Notebook trong môi trường Conda và thêm Kernel

Hướng dẫn xây dựng ứng dụng dự đoán giá cổ phiếu bằng Python

Hướng dẫn xây dựng ứng dụng dự đoán giá cổ phiếu bằng Python

Hướng dẫn tạo ứng dụng AI hội thoại với NVIDIA Jarvis trong Python

Hướng dẫn tạo ứng dụng AI hội thoại với NVIDIA Jarvis trong Python

Hỗ trợ Async trong Django 3.1

Hỗ trợ Async trong Django 3.1

8 mẹo tái cấu trúc Python giúp mã sạch hơn và Pythonic

8 mẹo tái cấu trúc Python giúp mã sạch hơn và Pythonic

Ý nghĩa của if __name__ ==

Ý nghĩa của if __name__ == "__main__" trong Python

Cách xóa phần tử trong danh sách Python

Cách xóa phần tử trong danh sách Python

Top