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! |