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 lưu trữ và tải lại Models trong PyTorch

Cách lưu trữ và tải lại Models trong PyTorch

Tìm hiểu về TensorBoard với PyTorch

Tìm hiểu về TensorBoard với PyTorch

Học chuyển giao (Transfer Learning) trong PyTorch Beginner

Học chuyển giao (Transfer Learning) trong PyTorch Beginner

Hướng dẫn cơ bản mạng Nơ-ron Tích Chập (CNN) trong PyTorch

Hướng dẫn cơ bản mạng Nơ-ron Tích Chập (CNN) trong PyTorch

Mạng Nơ-Ron truyền thẳng (Feed Forward Neural Network) trong PyTorch

Mạng Nơ-Ron truyền thẳng (Feed Forward Neural Network) trong PyTorch

Tìm hiểu Activation Functions trong PyTorch

Tìm hiểu Activation Functions trong PyTorch

Softmax và Cross Entropy trong PyTorch Beginner

Softmax và Cross Entropy trong PyTorch Beginner

Dataset Transforms trong PyTorch Beginner

Dataset Transforms trong PyTorch Beginner

Dataset và DataLoader trong PyTorch Beginner

Dataset và DataLoader trong PyTorch Beginner

Hồi quy Logistic trong PyTorch Beginner

Hồi quy Logistic trong PyTorch Beginner

Hồi quy tuyến tính trong PyTorch Beginner

Hồi quy tuyến tính trong PyTorch Beginner

Training Pipeline trong PyTorch Beginner

Training Pipeline trong PyTorch Beginner

Sử dụng Gradient Descent với Autograd trong PyTorch

Sử dụng Gradient Descent với Autograd trong PyTorch

Hướng dẫn về Tensor cơ bản trong PyTorch

Hướng dẫn về Tensor cơ bản trong PyTorch

Hướng dẫn cài đặt PyTorch với Deep Learning

Hướng dẫn cài đặt PyTorch với Deep Learning

LDA (Linear Discriminant Analysis) trong Python

LDA (Linear Discriminant Analysis) trong Python

Thuật toán AdaBoost trong Python

Thuật toán AdaBoost trong Python

Thuật toán K-Means Clustering trong Python

Thuật toán K-Means Clustering trong Python

Triển khai PCA bằng Python

Triển khai PCA bằng Python

Triển khai thuật toán Random Forest bằng Python

Triển khai thuật toán Random Forest bằng Python

Top