1. Khái niệm đa luồng trong Python

Đa luồng chính là việc chạy các chương trình khác nhau trong cùng một thời điểm. Một ví dụ điển hình về đa luồng như trong một máy tính, có thể đồng thời ta có thể sử dụng các phần mềm A, phần mềm B…. trong cùng lúc hay mở chúng song song với nhau. Trong Python, việc chạy các chương trình cùng một thời điểm (đa luồng) sẽ mang lại nhiều lợi ích khác nhau như:

  • Cùng chia sẻ một không gian dữ liệu với luồng chính và do đó có thể chia sẻ thông tin hoặc giao tiếp với nhau dễ dàng hơn so với việc các chương trình là các quy trình riêng biệt.
  • Các chương trình chạy nhẹ hơn và không yêu cầu cấp phát nhiều bộ nhớ.

Trong Python, cung cấp sẵn cho chúng ta thư viện để xử lý đa luồng. Thư viện đó chính là _thread và để sử dụng nó ta chỉ cần nhập thư viện có sẵn này vào chương trình như sau:

import _thread

Sau khi thư viện trên được nhập vào chương trình, ta hoàn toàn có thể sử dụng các phương thức có sẵn trong _thread để thực hiện xử lý các tác vụ đa luồng trong Python.

2. Tạo luồng mới (New Thread) trong Python

Các chương trình Python trước đây mà ta đã từng biết, chúng đều thực hiện chạy độc lập với nhau (vì chưa có sử dụng đa luồng). Trong một số trường hợp hoặc một số yêu cầu bắt buộc, chúng ta cần phải chạy các chương trình một cách song song với nhau (hay chạy đa luồng) trong Python bằng cách tạo ra các luồng mới.

Như đã đề cập ở trên, thư viện _thread trong Python sẽ hỗ trợ ta các tác vụ đa luồng này. Trong đó, bắt đầu tạo ra một luồng mới (new thread) ta sẽ cần phải sử dụng hàm _thread.start_new_thread() để làm điều này – Hàm này được sử dụng một cách nhanh chóng và hiệu quả để tạo các luồng mới trong cả hệ điều hành Linux hay Windows. Cú pháp đầy đủ của hàm như sau:

_thread.start_new_thread ( function, args[, kwargs] )

Trong đó:

  • function là tên hàm được chạy trong luồng mới
  • args[, kwargs] là các tham số được truyền vào hàm

Một ví dụ dưới đây, sử dụng hàm _thread.start_new_thread() để thực hiện chạy đa luồng đối với hàm có tên là print_time(tenLuong, delay) và các tham số truyền vào hàm là tenLuong, delay

import _thread
import time

# Dinh nghia mot ham duoc chay trong luong moi
def print_time(tenLuong, delay):
    count = 0
    while count < 5:
        time.sleep(delay)
        count += 1
        print ("{0}: {1}".format(tenLuong, time.ctime(time.time())))

# Tao hai luong song song va thuc thi chung
try:
   _thread.start_new_thread(print_time, ("Luong 1", 2,))
   _thread.start_new_thread(print_time, ("Luong 2", 4,))
except:
   print ("Loi: Khong the bat dau luong moi")

while 1:
   pass

Kết quả:

Luong 1: Mon May 23 18:39:33 2022
Luong 2: Mon May 23 18:39:35 2022
Luong 1: Mon May 23 18:39:35 2022
Luong 1: Mon May 23 18:39:37 2022
Luong 2: Mon May 23 18:39:39 2022
Luong 1: Mon May 23 18:39:39 2022
Luong 1: Mon May 23 18:39:41 2022
Luong 2: Mon May 23 18:39:43 2022
Luong 2: Mon May 23 18:39:47 2022
Luong 2: Mon May 23 18:39:51 2022

Có thể thấy rằng ở ví dụ trên, ta chạy hàm print_time() bằng cách chạy đồng thời và song song với nhau. Các điểm thời gian của luồng 1 trong hàm print_time() được chạy cách nhau 2 giây và các điểm thời gian của luồng 2 trong hàm print_time() được chạy cách nhau 4 giây.

3. Mô-đun Threading trong Python

3.1 Tổng quan mô-đun Threading trong Python

Mô đun Threading được coi là thư viện xử lý phân luồng cao cấp hơn so với _thread mà ở phần trước ta đã sử dụng để xử lý đa luồng. Để sử dụng được mô đun Threading ta cần nhập chúng vào trong chương trình Python cần sử dụng như sau:

import threading

Như đã đề cập, mô đun này là cao cấp hơn so với _thread vì vậy chúng cũng cung cấp thêm nhiều các lớp và phương thức để xử lý đa luồng hơn và được liệt kê ở bảng dưới đây:

Phương thức Chức năng
threading.activeCount() Trả về số đối tượng luồng đang hoạt động.
threading.currentThread() Trả về số đối tượng luồng trong điều khiển luồng của người gọi.
threading.enumerate() Trả về danh sách tất cả các đối tượng luồng hiện đang hoạt động.

Ngoài các phương thức trên, trong mô đun Threading còn có một lớp Thread để triển khai đa luồng. Trong lớp Thread này cũng có các phương thức sau:

run() Khởi tạo điểm vào của một luồng
start() Bắt đầu thực thi một luồng
join ([time]) Đợi cho các thread kết thúc.
isAlive() Kiểm tra xem một thread có đang thực thi hay không.
getName() Trả về tên của một luồng.
setName() Đặt tên cho một luồng.

3.2 Tạo luồng mới bằng mô đun Threading trong Python

Để tạo một luồng mới bằng cách sử dụng mô đun Threading, chúng ta cần thực hiện 3 bước chính được miêu tả như sau:

  • Bước 1: Định nghĩa một lớp con mới kế thừa lại lớp Thread.
  • Bước 2: Ghi đè phương thức khởi tạo __init __ (self [, args]) để thêm các đối số bổ sung.
  • Bước 3: Ghi đè phương thức run (self [, args]) để triển khai những gì luồng sẽ làm khi bắt đầu.

Các lớp con được kế thừa lại từ lớp Thread đều có thể đươc thể hiện lại thành một cách riêng và để thực hiện phân luồng cho lớp đó ta sẽ gọi thông qua phương thức start() và sau đó gọi đến phương thức run().

import threading
import time

exitFlag = 0

class myThread(threading.Thread):
   def __init__(self, idLuong, tenLuong, soLuong):
      threading.Thread.__init__(self)
      self.threadID = idLuong
      self.name = tenLuong
      self.counter = soLuong
   def run(self):
      print("Bat dau luong: " + self.name)
      print_time(self.name, 5, self.counter)
      print ("Ket thuc luong: " + self.name)

def print_time(tenLuong, soLuong, delay):
   while soLuong:
      if exitFlag:
         tenLuong.exit()
      time.sleep(delay)
      print ("{0}: {1}".format(tenLuong, time.ctime(time.time())))
      soLuong -= 1

# Tao luong moi
luong1 = myThread(1, "Luong-1", 1)
luong2 = myThread(2, "Luong-2", 2)

# Bat dau luong moi
luong1.start()
luong2.start()

print("Thoat khoi luong chinh!")

Kết quả:

Bat dau luong: Luong-1
Bat dau luong: Luong-2
Thoat khoi luong chinh!
Luong-1: Tue May 24 12:38:12 2022
Luong-2: Tue May 24 12:38:13 2022
Luong-1: Tue May 24 12:38:13 2022
Luong-1: Tue May 24 12:38:14 2022
Luong-2: Tue May 24 12:38:15 2022
Luong-1: Tue May 24 12:38:15 2022
Luong-1: Tue May 24 12:38:16 2022
Ket thuc luong: Luong-1
Luong-2: Tue May 24 12:38:17 2022
Luong-2: Tue May 24 12:38:19 2022
Luong-2: Tue May 24 12:38:21 2022
Ket thuc luong: Luong-2

3.3 Đồng bộ hóa các luồng trong Python

Mô-đun Threading được cung cấp với Python bao gồm cơ chế khóa đơn giản để triển khai cho phép bạn đồng bộ hóa các luồng. Một khóa mới được tạo bằng cách gọi phương thức lock(). Phương thức acquire(blocking) của đối tượng khóa mới được sử dụng để buộc các luồng chạy đồng bộ. Tham số blocking của phương thức này là tùy chọn, chúng cho phép bạn kiểm soát xem luồng có chờ lấy khóa hay không.

  • Nếu tham số blocking = 0 thì luồng sẽ trả về 2 giá trị đó là: 0 hoặc 1. Nếu phương thức trả về 0 nghĩa là không thể lấy được khóa và với giá trị 1 nếu đã có khóa.
  • Nếu tham số blocking = 1 thì luồng sẽ bị chặn lại và chờ đợi khóa được giải phóng.

Cuối cùng, phương thức release() của đối tượng khóa mới được sử dụng để giải phóng khóa khi nó không còn được yêu cầu.

import threading
import time

class myThread(threading.Thread):
   def __init__(self, idLuong, tenLuong, soLuong):
      threading.Thread.__init__(self)
      self.threadID = idLuong
      self.name = tenLuong
      self.counter = soLuong
   def run(self):
      print ("Bat dau luong " + self.name)
      # Tao khoa de dong bo luong
      threadLock.acquire()
      print_time(self.name, self.counter, 3)
      # Giai phong khoa de chuyen sang luong tiep theo
      threadLock.release()

def print_time(tenLuong, delay, soLuong):
   while soLuong:
      time.sleep(delay)
      print ("{0}: {1}".format(tenLuong, time.ctime(time.time())))
      soLuong -= 1

threadLock = threading.Lock()
threads = []

# Tao luong moi
thread1 = myThread(1, "Luong-1", 1)
thread2 = myThread(2, "Luong-2", 2)

# Bat dau luong moi
thread1.start()
thread2.start()

# Them cac luong moi vao trong danh sach luong 
threads.append(thread1)
threads.append(thread2)

# Cho doi tat ca cac luong duoc hoan thanh
for t in threads:
    t.join()
print ("Thoat khoi luong chinh!")

Kết quả:

Bat dau luong Luong-1
Bat dau luong Luong-2
Luong-1: Tue May 24 12:56:07 2022
Luong-1: Tue May 24 12:56:08 2022
Luong-1: Tue May 24 12:56:09 2022
Luong-2: Tue May 24 12:56:11 2022
Luong-2: Tue May 24 12:56:13 2022
Luong-2: Tue May 24 12:56:15 2022
Thoat khoi luong chinh!