Trong Python, đa luồng cho phép bạn chạy nhiều luồng đồng thời trong một tiến trình duy nhất, điều này còn được gọi là song song dựa trên luồng. Điều này có nghĩa là một chương trình có thể thực hiện nhiều tác vụ cùng một lúc, nâng cao hiệu suất và khả năng phản hồi của nó.
Đa luồng trong Python đặc biệt hữu ích cho nhiều thao tác liên quan đến I/O, thay vì cho các tác vụ yêu cầu tính toán nặng.
Nói chung, một chương trình máy tính thực hiện tuần tự các lệnh, từ đầu đến cuối. Trong khi đó, Đa luồng (Multithreading) chia nhiệm vụ chính thành nhiều nhiệm vụ con và thực hiện chúng theo cách chồng chéo.
Hệ điều hành có khả năng xử lý nhiều tiến trình đồng thời. Nó phân bổ một không gian bộ nhớ riêng biệt cho mỗi tiến trình để một tiến trình không thể truy cập hoặc ghi bất kỳ thứ gì vào không gian của tiến trình khác.
Mặt khác, một luồng có thể được coi là một tiểu quy trình nhẹ trong một chương trình đơn lẻ chia sẻ không gian bộ nhớ được phân bổ cho nó, giúp việc giao tiếp và chia sẻ dữ liệu trở nên dễ dàng hơn. Vì chúng nhẹ và không yêu cầu nhiều bộ nhớ bổ sung; chúng rẻ hơn so với các quy trình.
Một quá trình luôn bắt đầu với một luồng đơn (luồng chính). Khi cần thiết, một luồng mới có thể được khởi động và nhiệm vụ phụ sẽ được giao cho nó. Bây giờ, hai luồng đang làm việc một cách chồng chéo với nhau. Khi nhiệm vụ được giao cho luồng phụ hoàn thành, nó sẽ hợp nhất với luồng chính.
Một luồng (thread) có một điểm bắt đầu, một chuỗi thực hiện và một kết thúc. Nó có một con trỏ lệnh (instruction pointer) theo dõi vị trí mà nó đang chạy trong ngữ cảnh của nó.
Nó có thể bị tạm dừng (gián đoạn).
Nó có thể tạm thời bị tạm dừng (còn được gọi là ngủ) trong khi các luồng khác đang chạy - điều này được gọi là nhường CPU.
Thư viện chuẩn của Python cung cấp hai mô-đun chính để quản lý luồng: _thread và threading .
Mô-đun _thread, còn được biết đến là mô-đun luồng cấp thấp, đã trở thành một phần của thư viện chuẩn của Python kể từ phiên bản 2. Nó cung cấp một API cơ bản để quản lý luồng, hỗ trợ thực thi đồng thời các luồng trong một không gian dữ liệu toàn cục chia sẻ. Mô-đun này bao gồm các khóa đơn giản (mutexes) để mục đích đồng bộ hóa.
Mô-đun threading, được giới thiệu trong Python 2.4, xây dựng dựa trên _thread để cung cấp một API đa luồng cao cấp và toàn diện hơn. Nó cung cấp các công cụ mạnh mẽ để quản lý các luồng, giúp việc làm việc với các luồng trong các ứng dụng Python trở nên dễ dàng hơn.
Key Features of the threading Module
Mô-đun threading cung cấp tất cả các phương thức của mô-đun thread và cung cấp một số phương thức bổ sung.
Ngoài các phương thức, mô-đun threading có lớp Thread thực hiện việc đa luồng. Các phương thức được cung cấp bởi lớp Thread như sau −
Để tạo và bắt đầu một luồng mới trong Python, bạn có thể sử dụng mô-đun _thread ở mức thấp hoặc mô-đun threading ở mức cao hơn. Mô-đun threading thường được khuyến nghị do các tính năng bổ sung và dễ sử dụng hơn. Dưới đây, bạn có thể thấy cả hai cách tiếp cận.
Phương thức start_new_thread() của mô-đun _thread cung cấp một cách cơ bản để tạo và khởi động các luồng mới. Phương thức này cung cấp một cách nhanh chóng và hiệu quả để tạo ra các luồng mới trên cả Linux và Windows. Dưới đây là cú pháp của phương thức −
thread.start_new_thread(function, args[, kwargs] )
Cuộc gọi phương thức này trả về ngay lập tức, và luồng mới bắt đầu thực thi hàm đã chỉ định với các đối số đã cho. Khi hàm trả về, luồng sẽ kết thúc.
Example
Ví dụ này minh họa cách sử dụng mô-đun _thread để tạo và chạy các luồng. Mỗi luồng chạy hàm print_name với các đối số khác nhau. Lệnh time.sleep(0.5) đảm bảo rằng chương trình chính chờ các luồng hoàn thành thực thi trước khi thoát.
import _thread import time def print_name(name, *arg): print(name, *arg) name="Tutorialspoint..." _thread.start_new_thread(print_name, (name, 1)) _thread.start_new_thread(print_name, (name, 1, 2)) time.sleep(0.5)
Khi đoạn mã trên được thực thi, nó sẽ tạo ra kết quả sau −
Tutorialspoint... 1 Tutorialspoint... 1 2
Mặc dù nó rất hiệu quả cho đa luồng cấp thấp, nhưng mô-đun _thread bị hạn chế so với mô-đun threading , cung cấp nhiều tính năng hơn và quản lý luồng cấp cao hơn.
Mô-đun threading cung cấp lớp Thread , được sử dụng để tạo và quản lý các luồng.
Dưới đây là một vài bước để bắt đầu một luồng mới bằng cách sử dụng mô-đun threading −
Example
Ví dụ dưới đây minh họa cách tạo và khởi động các luồng sử dụng mô-đun threading. Nó chạy một hàm print_name để in một tên cùng với một số đối số. Ví dụ này tạo ra hai luồng, khởi động chúng bằng cách sử dụng phương thức start(), và chờ chúng hoàn thành bằng phương thức join .
import threading import time def print_name(name, *args): print(name, *args) name = "Tutorialspoint..." # Create and start threads thread1 = threading.Thread(target=print_name, args=(name, 1)) thread2 = threading.Thread(target=print_name, args=(name, 1, 2)) thread1.start() thread2.start() # Wait for threads to complete thread1.join() thread2.join() print("Threads are finished...exiting")
Khi đoạn mã trên được thực thi, nó sẽ tạo ra kết quả sau −
Tutorialspoint... 1 Tutorialspoint... 1 2 Threads are finished...exiting
Mô-đun threading được cung cấp cùng với Python bao gồm một cơ chế khóa dễ 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 ra bằng cách gọi phương thức Lock() , phương thức này trả về khóa mới.
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ố tùy chọn blocking cho phép bạn kiểm soát xem luồng có chờ để chiếm khóa hay không.
Nếu blocking được đặt thành 0, luồng sẽ trả về ngay lập tức với giá trị 0 nếu không thể chiếm được khóa và với giá trị 1 nếu khóa đã được chiếm. Nếu blocking được đặt thành 1, luồng sẽ bị chặn và chờ cho đến khi khóa được giải phó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 không còn cần thiết nữa.
import threading import time class myThread (threading.Thread): def __init__(self, threadID, name, counter): threading.Thread.__init__(self) self.threadID = threadID self.name = name self.counter = counter def run(self): print ("Starting " + self.name) # Get lock to synchronize threads threadLock.acquire() print_time(self.name, self.counter, 3) # Free lock to release next thread threadLock.release() def print_time(threadName, delay, counter): while counter: time.sleep(delay) print ("%s: %s" % (threadName, time.ctime(time.time()))) counter -= 1 threadLock = threading.Lock() threads = [] # Create new threads thread1 = myThread(1, "Thread-1", 1) thread2 = myThread(2, "Thread-2", 2) # Start new Threads thread1.start() thread2.start() # Add threads to thread list threads.append(thread1) threads.append(thread2) # Wait for all threads to complete for t in threads: t.join() print ("Exiting Main Thread")
Khi đoạn mã trên được thực thi, nó sẽ tạo ra kết quả sau −
Starting Thread-1 Starting Thread-2 Thread-1: Thu Mar 21 09:11:28 2013 Thread-1: Thu Mar 21 09:11:29 2013 Thread-1: Thu Mar 21 09:11:30 2013 Thread-2: Thu Mar 21 09:11:32 2013 Thread-2: Thu Mar 21 09:11:34 2013 Thread-2: Thu Mar 21 09:11:36 2013 Exiting Main Thread
Mô-đun Queue cho phép bạn tạo một đối tượng hàng đợi mới có thể chứa một số lượng mục cụ thể. Có các phương thức sau để điều khiển hàng đợi −
get() − Hàm get() sẽ loại bỏ và trả về một mục từ hàng đợi.
put() − Lệnh put thêm mục vào hàng đợi.
qsize() − Hàm qsize() trả về số lượng mục hiện có trong hàng đợi.
empty() − Hàm empty( ) trả về True nếu hàng đợi rỗng; ngược lại, trả về False.
full() − hàm full() trả về True nếu hàng đợi đầy; ngược lại, trả về False.
import queue import threading import time exitFlag = 0 class myThread (threading.Thread): def __init__(self, threadID, name, q): threading.Thread.__init__(self) self.threadID = threadID self.name = name self.q = q def run(self): print ("Starting " + self.name) process_data(self.name, self.q) print ("Exiting " + self.name) def process_data(threadName, q): while not exitFlag: queueLock.acquire() if not workQueue.empty(): data = q.get() queueLock.release() print ("%s processing %s" % (threadName, data)) else: queueLock.release() time.sleep(1) threadList = ["Thread-1", "Thread-2", "Thread-3"] nameList = ["One", "Two", "Three", "Four", "Five"] queueLock = threading.Lock() workQueue = queue.Queue(10) threads = [] threadID = 1 # Create new threads for tName in threadList: thread = myThread(threadID, tName, workQueue) thread.start() threads.append(thread) threadID += 1 # Fill the queue queueLock.acquire() for word in nameList: workQueue.put(word) queueLock.release() # Wait for queue to empty while not workQueue.empty(): pass # Notify threads it's time to exit exitFlag = 1 # Wait for all threads to complete for t in threads: t.join() print ("Exiting Main Thread")
Khi đoạn mã trên được thực thi, nó sẽ tạo ra kết quả sau −
Starting Thread-1 Starting Thread-2 Starting Thread-3 Thread-1 processing One Thread-2 processing Two Thread-3 processing Three Thread-1 processing Four Thread-2 processing Five Exiting Thread-3 Exiting Thread-1 Exiting Thread-2 Exiting Main Thread