Python - Inter

Giao tiếp giữa các luồng (Inter-Thread Communication) đề cập đến quá trình cho phép giao tiếp và đồng bộ hóa giữa các luồng trong một chương trình đa luồng của Python.

Nói chung, các luồng trong Python chia sẻ cùng một không gian bộ nhớ trong một tiến trình, điều này cho phép chúng trao đổi dữ liệu và phối hợp hoạt động của mình thông qua các biến, đối tượng và cơ chế đồng bộ hóa chuyên dụng được cung cấp bởi mô-đun threading .

Để tạo điều kiện cho việc giao tiếp giữa các luồng, mô-đun threading cung cấp nhiều nguyên tắc đồng bộ hóa khác nhau như: đối tượng Locks, Events, Conditions và Semaphores. Trong hướng dẫn này, bạn sẽ học cách sử dụng đối tượng Event và Condition để cung cấp giao tiếp giữa các luồng trong một chương trình đa luồng.

The Event Object

Một đối tượng Event quản lý trạng thái của một cờ nội bộ để các luồng có thể chờ hoặc thiết lập. Đối tượng Event cung cấp các phương thức để kiểm soát trạng thái của cờ này, cho phép các luồng đồng bộ hóa hoạt động của họ dựa trên các điều kiện chia sẻ.

Cờ ban đầu là false và trở thành true với phương thức set() và được thiết lập lại thành false với phương thức clear(). Phương thức wait() sẽ chặn cho đến khi cờ trở thành true.

Các phương thức chính của đối tượng Event như sau −

  • is_set() : Return True if and only if the internal flag is true.
  • set() : Set the internal flag to true. All threads waiting for it to become true are awakened. Threads that call wait() once the flag is true will not block at all.
  • clear() : Reset the internal flag to false. Subsequently, threads calling wait() will block until set() is called to set the internal flag to true again.
  • wait(timeout=None) : Block until the internal flag is true. If the internal flag is true on entry, return immediately. Otherwise, block until another thread calls set() to set the flag to true, or until the optional timeout occurs. When the timeout argument is present and not None, it should be a floating point number specifying a timeout for the operation in seconds.

Example

Đoạn mã sau cố gắng mô phỏng dòng giao thông được điều khiển bởi trạng thái của tín hiệu giao thông, hoặc là XANH (GREEN) hoặc ĐỎ (RED).

Có hai luồng trong chương trình, nhắm đến hai hàm khác nhau. Hàm signal_state() định kỳ thiết lập và đặt lại sự kiện cho biết sự thay đổi tín hiệu từ XANH sang ĐỎ.

Hàm traffic_flow() chờ sự kiện được thiết lập và chạy một vòng lặp cho đến khi nó vẫn được thiết lập.

from threading import Event, Thread
import time

terminate = False

def signal_state():
    global terminate
    while not terminate:
        time.sleep(0.5)
        print("Traffic Police Giving GREEN Signal")
        event.set()
        time.sleep(1)
        print("Traffic Police Giving RED Signal")
        event.clear()

def traffic_flow():
    global terminate
    num = 0
    while num < 10 and not terminate:
        print("Waiting for GREEN Signal")
        event.wait()
        print("GREEN Signal ... Traffic can move")
        while event.is_set() and not terminate:
            num += 1
            print("Vehicle No:", num," Crossing the Signal")
            time.sleep(1)
        print("RED Signal ... Traffic has to wait")

event = Event()
t1 = Thread(target=signal_state)
t2 = Thread(target=traffic_flow)
t1.start()
t2.start()

# Terminate the threads after some time
time.sleep(5)
terminate = True

# join all threads to complete
t1.join()
t2.join()

print("Exiting Main Thread")

Output

Khi thực thi đoạn mã trên, bạn sẽ nhận được đầu ra sau:

Waiting for GREEN Signal
Traffic Police Giving GREEN Signal
GREEN Signal ... Traffic can move
Vehicle No: 1  Crossing the Signal
Traffic Police Giving RED Signal
RED Signal ... Traffic has to wait
Waiting for GREEN Signal
Traffic Police Giving GREEN Signal
GREEN Signal ... Traffic can move
Vehicle No: 2  Crossing the Signal
Vehicle No: 3  Crossing the Signal
Traffic Police Giving RED Signal
Traffic Police Giving GREEN Signal
Vehicle No: 4  Crossing the Signal
Traffic Police Giving RED Signal
RED Signal ... Traffic has to wait
Traffic Police Giving GREEN Signal
Traffic Police Giving RED Signal
Exiting Main Thread

The Condition Object

Đối tượng Condition trong mô-đun threading của Python cung cấp một cơ chế đồng bộ hóa nâng cao hơn. Nó cho phép các luồng chờ đợi một thông báo từ một luồng khác trước khi tiếp tục. Đối tượng Condition luôn được liên kết với một khóa và cung cấp các cơ chế để báo hiệu giữa các luồng.

Dưới đây là cú pháp của lớp threading.Condition() −

threading.Condition(lock=None)

Dưới đây là các phương thức chính của đối tượng Condition −

  • acquire(*args) : Acquire the underlying lock. This method calls the corresponding method on the underlying lock; the return value is whatever that method returns.
  • release() : Release the underlying lock. This method calls the corresponding method on the underlying lock; there is no return value.
  • wait(timeout=None) : This method releases the underlying lock, and then blocks until it is awakened by a notify() or notify_all() call for the same condition variable in another thread, or until the optional timeout occurs. Once awakened or timed out, it re-acquires the lock and returns.
  • wait_for(predicate, timeout=None) : This utility method may call wait() repeatedly until the predicate is satisfied, or until a timeout occurs. The return value is the last return value of the predicate and will evaluate to False if the method timed out.
  • notify(n=1) : This method wakes up at most n of the threads waiting for the condition variable; it is a no-op if no threads are waiting.
  • notify_all() : Wake up all threads waiting on this condition. This method acts like notify(), but wakes up all waiting threads instead of one. If the calling thread has not acquired the lock when this method is called, a RuntimeError is raised.

Example

Ví dụ này minh họa một hình thức giao tiếp giữa các luồng đơn giản sử dụng đối tượng Condition của module threading trong Python. Ở đây, thread_a thread_b được giao tiếp bằng cách sử dụng một đối tượng Condition, thread_a chờ cho đến khi nhận được thông báo từ thread_b . thread_b ngủ trong 2 giây trước khi thông báo cho thread_a và sau đó kết thúc.

from threading import Condition, Thread
import time

c = Condition()

def thread_a():
    print("Thread A started")
    with c:
        print("Thread A waiting for permission...")
        c.wait()
        print("Thread A got permission!")
    print("Thread A finished")

def thread_b():
    print("Thread B started")
    with c:
        time.sleep(2)
        print("Notifying Thread A...")
        c.notify()
    print("Thread B finished")

Thread(target=thread_a).start()
Thread(target=thread_b).start()

Output

Khi thực thi đoạn mã trên, bạn sẽ nhận được đầu ra sau:

Thread A started
Thread A waiting for permission...
Thread B started
Notifying Thread A...
Thread B finished
Thread A got permission!
Thread A finished

Example

Dưới đây là một đoạn mã khác minh họa cách đối tượng Condition được sử dụng để cung cấp giao tiếp giữa các luồng. Trong đoạn mã này, luồng t2 thực hiện hàm taskB(), và luồng t1 thực hiện hàm taskA(). Luồng t1 chiếm điều kiện và thông báo cho nó.

Vào thời điểm đó, luồng t2 đang ở trạng thái chờ. Sau khi điều kiện được giải phóng, luồng đang chờ sẽ tiếp tục tiêu thụ số ngẫu nhiên được tạo ra bởi hàm thông báo.

from threading import Condition, Thread
import time
import random

numbers = []

def taskA(c):
    for _ in range(5):
        with c:
            num = random.randint(1, 10)
            print("Generated random number:", num)
            numbers.append(num)
            print("Notification issued")
            c.notify()
        time.sleep(0.3)

def taskB(c):
    for i in range(5):
        with c:
            print("waiting for update")
            while not numbers: 
                c.wait()
            print("Obtained random number", numbers.pop())
        time.sleep(0.3)

c = Condition()
t1 = Thread(target=taskB, args=(c,))
t2 = Thread(target=taskA, args=(c,))
t1.start()
t2.start()
t1.join()
t2.join()
print("Done")

Khi bạn thực thi đoạn mã này, nó sẽ tạo ra kết quả sau output

waiting for update
Generated random number: 2
Notification issued
Obtained random number 2
Generated random number: 5
Notification issued
waiting for update
Obtained random number 5
Generated random number: 1
Notification issued
waiting for update
Obtained random number 1
Generated random number: 9
Notification issued
waiting for update
Obtained random number 9
Generated random number: 2
Notification issued
waiting for update
Obtained random number 2
Done