Python - Lập trình đa luồng
Bài đăng này đã không được cập nhật trong 6 năm
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
All rights reserved