Python - Lập trình đa luồng
Khi ta chạy một số thread cũng giống như chạy nhiều chương trình khác nhau đồng thời, nhưng nó đem lại một số lợi ích như sau: Đa luồng trong một tiến trình chia sẻ cùng một không gian dữ liệu với main thread do đó có thể chia sẻ dữ liệu, kết nối với nhau dễ dàng hơn là các chương trình riêng ...
Khi ta chạy một số thread cũng giống như chạy nhiều chương trình khác nhau đồng thời, nhưng nó đem lại một số lợi ích như sau:
- Đa luồng trong một tiến trình chia sẻ cùng một không gian dữ liệu với main thread do đó có thể chia sẻ dữ liệu, kết nối với nhau dễ dàng hơn là các chương trình riêng biệt
- Các thread đôi khi được gọi là các tiến trình light-weight và chúng không đòi hỏi nhiều bộ nhớ, chúng tốn ít hơn tiến trình.
Một thread có một khởi động, một chu trình và một kết thúc. Nó có con trỏ để chỉ dẫn theo dõi vị trí trong ngữ cảnh mà nó đang chạy.
- Nó có thể làm gián đoạn
- Và cũng có thể được giữ lại(trong trạng thái sleep) trong khi các thread khác đang chạy. Nó được gọi là nhượng lại
Start một New Thread
Để sinh ra một luồng khác, ta gọi phương thức sau trong module thread
thread.start_new_thread ( function, args[, kwargs] )
Phương thức này cho phép khởi tạo một thread nhanh chóng mà hiệu quả trong cả môi trường Linux và Windows Phương thức gọi trả về ngay lập tức và các thread con bắt đầu gọi hàm với danh sách của args. Khi func retrurn thì thread kết thúc. Ở đây args là một tuple. Nó rỗng khi không truyền bất kỳ đối số nào, kwargs là 1 dictionary optional của keyword arguments Ví dụ:
#!/usr/bin/python import thread import time # Khai báo một function cho thread def print_time( threadName, delay): count = 0 while count < 5: time.sleep(delay) count += 1 print "%s: %s" % ( threadName, time.ctime(time.time()) ) # Tạo 2 thread try: thread.start_new_thread( print_time, ("Thread-1", 2, ) ) thread.start_new_thread( print_time, ("Thread-2", 4, ) ) except: print "Error: unable to start thread" while 1: pass
Khi đoạn code trên được thực thi thì:
Thread-1: Thu Mar 22 15:42:17 2018 Thread-1: Thu Mar 22 15:42:19 2018 Thread-2: Thu Mar 22 15:42:19 2018 Thread-1: Thu Mar 22 15:42:21 2018 Thread-2: Thu Mar 22 15:42:23 2018 Thread-1: Thu Mar 22 15:42:23 2018 Thread-1: Thu Mar 22 15:42:25 2018 Thread-2: Thu Mar 22 15:42:27 2018 Thread-2: Thu Mar 22 15:42:31 2018 Thread-2: Thu Mar 22 15:42:35 2018
Mặc dù nó hiệu quả cho các luồng ở mức low-level nhưng module thread rất hạn chế so với module mới là threading module
Threading module
Threading module hỗ trợ mạnh mẽ hơn so với module thread được đề cập ở phần trước Chúng bao gồm tất cả các method của module thread và có thêm một số phần mở rộng như:
- threading.activeCount(): Trả về số lượng thread đang hoạt động
- threading.currentThread(): Trả về số lượng đối tượng trong chuỗi điều khiển luồng được gọi
- threading.enumerate(): Trả về danh sách tất cả các đối tượng chuỗi hiện đang hoạt động.
Ngoài các method thì threading module còn có lớp Thread để thực hiện luồng, class có các method sau:
- run() − Là entry point cho một thread
- start() − Start một thread bằng cách gọi method run()
- join([time]) − The join() waits for threads to terminate. Chờ cho các thread đóng với tham số time
- isAlive() − Kiểm tra một thread vẫn còn đang thực thi
- getName() − Trả về tên của thread
- setName() − Set tên cho thread
Tạo thread bằng Threading module
Để tạo một thread sử dụng threading module, ta phải thực hiện các bước sau:
- Khởi tạo một subclass của class Thread
- Override func init(self [,args]) để thêm đối số
- Override the run(self [,args]) method to implement what the thread should do when started. Override func run(self [,args]) để thực hiện những gì các thread nên làm gì khi bắt đầu.
Một khi ta subclass của class Thread, ta có thể tạo một instance của nó và bắt đầu một thread mới bằng cách gọi method start() mà lần lượt là run()
#!/usr/bin/python import threading import time exitFlag = 0 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 print_time(self.name, 5, self.counter) print "Exiting " + self.name def print_time(threadName, counter, delay): while counter: if exitFlag: threadName.exit() time.sleep(delay) print "%s: %s" % (threadName, time.ctime(time.time())) counter -= 1 # Tạo thread mới thread1 = myThread(1, "Thread-1", 1) thread2 = myThread(2, "Thread-2", 2) # Start thread mới thread1.start() thread2.start() print "Exiting Main Thread"
Và kết quả là:
Starting Thread-1 Starting Thread-2 Exiting Main Thread Thread-1: Thu Mar 21 09:10:03 2018 Thread-1: Thu Mar 21 09:10:04 2018 Thread-2: Thu Mar 21 09:10:04 2018 Thread-1: Thu Mar 21 09:10:05 2018 Thread-1: Thu Mar 21 09:10:06 2018 Thread-2: Thu Mar 21 09:10:06 2018 Thread-1: Thu Mar 21 09:10:07 2018 Exiting Thread-1 Thread-2: Thu Mar 21 09:10:08 2018 Thread-2: Thu Mar 21 09:10:10 2018 Thread-2: Thu Mar 21 09:10:12 2018 Exiting Thread-2
Synchronizing Threads
Với bất kỳ ngôn ngữ nào làm việc với thread đều cũng đã từng động đến đồng bộ các threads. Vậy làm thế nào thực hiện nó đối với Python Threading module cung cấp cơ chế khoá đơn giản để triển khai cho phép ta đồng bộ các thread. Một khoá mới được tạo ra bằng cách gọi method Lock(), nó trả về một khoá mới. Method arquire(blocking) của khoá mới được sử dụng để ràng buộc các thread chạy đồng bộ. Tham số tuỳ chọn blocking cho phép ta kiểm soát thread có chờ khoá hay không. Nếu blocking đặt là 0, thread return ngay lập tức với gía trị 0 nếu không thể lấy khoá và 1 nếu khoá được thu hồi. Nếu blocking được đặt thành 1, chuỗi sẽ bị khóa và chờ 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 nó không còn cần thiết nữa Ví dụ:
#!/usr/bin/python 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 = [] # Tạo mới threads thread1 = myThread(1, "Thread-1", 1) thread2 = myThread(2, "Thread-2", 2) # Start newThreads 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"
Kết quả là
Starting Thread-1 Starting Thread-2 Thread-1: Thu Mar 21 09:11:28 2018 Thread-1: Thu Mar 21 09:11:29 2018 Thread-1: Thu Mar 21 09:11:30 2018 Thread-2: Thu Mar 21 09:11:32 2018 Thread-2: Thu Mar 21 09:11:34 2018 Thread-2: Thu Mar 21 09:11:36 2018 Exiting Main Thread
Multithreaded Priority Queue
Module Queue cho phép ta create một objet queue, nó lưu giữ một số lượng item nhất định. Để kiểm soát queue ta có 1 số method sau:
- get() − Removes and returns an item from the queue. lấy item ra khỏi queue và return về item đó
- put() − Thêm item vào queue
- qsize() − Trả về số items trong queue hiện tại
- empty() − Return true nếu queue empty còn không là false
- full() − Return true nếu queue full ngược lại là false Ví dụ:
#!/usr/bin/python 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 vào queue queueLock.acquire() for word in nameList: workQueue.put(word) queueLock.release() # Chờ cho queue rỗng while not workQueue.empty(): pass # Thông báo cho thread để thoát exitFlag = 1 # Chờ tất cả thread hoàn thành for t in threads: t.join() print "Exiting Main Thread"
Kết quả:
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
Refs: Python Advanced