Python 3 - Lập trình đa luồng
Chạy một số luồng tương tự như chạy một số chương trình khác nhau đồng thời, nhưng với những lợi ích sau:
Nhiều luồng trong một quy trình chia sẻ cùng 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 nếu chúng là các quy trình riêng biệt.
Các luồng đôi khi được gọi là các quy trình nhẹ và chúng không yêu cầu nhiều bộ nhớ; chúng rẻ hơn các quy trình.
Một chuỗi có một phần mở đầu, một chuỗi thực thi và một phần kết luận. Nó có một con trỏ hướng dẫn theo dõi vị trí mà nó hiện đang chạy trong ngữ cảnh của nó.
Nó có thể được làm trống trước (ngắt quãng).
Nó có thể tạm thời được đặt ở trạng thái chờ (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à năng suất.
Có hai loại chủ đề khác nhau -
- chuỗi hạt nhân
- chủ đề người dùng
Kernel Threads là một phần của hệ điều hành, trong khi các luồng User-space không được triển khai trong kernel.
Có hai mô-đun hỗ trợ việc sử dụng các luồng trong Python3:
- _thread
- threading
Mô-đun luồng đã "không được chấp nhận" trong một thời gian dài. Người dùng được khuyến khích sử dụng mô-đun phân luồng thay thế. Do đó, trong Python 3, "luồng" mô-đun không còn khả dụng nữa. Tuy nhiên, nó đã được đổi tên thành "_thread" để tương thích ngược trong Python3.
Bắt đầu một chuỗi mới
Để sinh ra một luồng khác, bạn cần gọi phương thức sau có sẵn trong mô-đun luồng :
_thread.start_new_thread ( function, args[, kwargs] )
Lệnh gọi phương thức này cho phép một cách nhanh chóng và hiệu quả để tạo các luồng mới trong cả Linux và Windows.
Lời gọi phương thức trả về ngay lập tức và luồng con bắt đầu và gọi hàm với danh sách các args đã truyền . Khi hàm trả về, luồng kết thúc.
Ở đây, args là một loạt các đối số; sử dụng một bộ giá trị trống để gọi hàm mà không chuyển bất kỳ đối số nào. kwargs là một từ điển tùy chọn của các đối số từ khóa.
Thí dụ
#!/usr/bin/python3
import _thread
import time
# Define a function for the thread
def print_time( threadName, delay):
count = 0
while count < 5:
time.sleep(delay)
count += 1
print ("%s: %s" % ( threadName, time.ctime(time.time()) ))
# Create two threads as follows
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
Đầu ra
Khi đoạn mã trên được thực thi, nó tạo ra kết quả sau:
Thread-1: Fri Feb 19 09:41:39 2016
Thread-2: Fri Feb 19 09:41:41 2016
Thread-1: Fri Feb 19 09:41:41 2016
Thread-1: Fri Feb 19 09:41:43 2016
Thread-2: Fri Feb 19 09:41:45 2016
Thread-1: Fri Feb 19 09:41:45 2016
Thread-1: Fri Feb 19 09:41:47 2016
Thread-2: Fri Feb 19 09:41:49 2016
Thread-2: Fri Feb 19 09:41:53 2016
Chương trình đi trong một vòng lặp vô hạn. Bạn sẽ phải nhấn ctrl-c để dừng
Mặc dù nó rất hiệu quả đối với phân luồng cấp thấp, nhưng mô-đun luồng rất hạn chế so với mô-đun luồng mới hơn.
Mô-đun phân luồng
Mô-đun luồng mới hơn được bao gồm trong Python 2.4 cung cấp hỗ trợ cấp cao, mạnh mẽ hơn nhiều cho các luồng so với mô-đun luồng được thảo luận trong phần trước.
Các luồng mô-đun cho thấy tất cả các phương pháp của chủ đề mô-đun và cung cấp một số phương pháp bổ sung -
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 pháp, mô-đun threading có Chủ đề lớp mà thực hiện luồng. Các phương thức được cung cấp bởi lớp Thread như sau:
run() - Phương thức run () là điểm vào của một luồng.
start() - Phương thức start () bắt đầu một luồng bằng cách gọi phương thức run.
join([time]) - Tham gia () đợi các luồng kết thúc.
isAlive() - Phương thức isAlive () kiểm tra xem một luồng có còn đang thực thi hay không.
getName() - Phương thức getName () trả về tên của một luồng.
setName() - Phương thức setName () đặt tên của một luồng.
Tạo chủ đề bằng mô-đun phân luồng
Để triển khai một luồng mới bằng cách sử dụng mô-đun luồng, bạn phải làm như sau:
Định nghĩa một lớp con mới của lớp Thread .
Ghi đè phương thức __init __ (self [, args]) để thêm các đối số bổ sung.
Sau đó, ghi đè phương thức run (self [, args]) để triển khai những gì chuỗi sẽ làm khi bắt đầu.
Khi bạn đã tạo lớp con Thread mới , bạn có thể tạo một thể hiện của nó và sau đó bắt đầu một luồng mới bằng cách gọi start () , lần lượt gọi phương thức run () .
Thí dụ
#!/usr/bin/python3
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, self.counter, 5)
print ("Exiting " + self.name)
def print_time(threadName, delay, counter):
while counter:
if exitFlag:
threadName.exit()
time.sleep(delay)
print ("%s: %s" % (threadName, time.ctime(time.time())))
counter -= 1
# Create new threads
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)
# Start new Threads
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print ("Exiting Main Thread")
Kết quả
Khi chúng tôi chạy chương trình trên, nó tạo ra kết quả sau:
Starting Thread-1
Starting Thread-2
Thread-1: Fri Feb 19 10:00:21 2016
Thread-2: Fri Feb 19 10:00:22 2016
Thread-1: Fri Feb 19 10:00:22 2016
Thread-1: Fri Feb 19 10:00:23 2016
Thread-2: Fri Feb 19 10:00:24 2016
Thread-1: Fri Feb 19 10:00:24 2016
Thread-1: Fri Feb 19 10:00:25 2016
Exiting Thread-1
Thread-2: Fri Feb 19 10:00:26 2016
Thread-2: Fri Feb 19 10:00:28 2016
Thread-2: Fri Feb 19 10:00:30 2016
Exiting Thread-2
Exiting Main Thread
Đồng bộ hóa chủ đề
Mô-đun phân luồng đượ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 này sẽ trả về khóa mới.
Phương thức thu được (chặn) 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ố chặn tùy chọn cho phép bạn kiểm soát xem luồng có chờ lấy khóa hay không.
Nếu chặn được đặt thành 0, chuỗi sẽ trả về ngay lập tức với giá trị 0 nếu không thể lấy được khóa và với giá trị 1 nếu đã có khóa. Nếu chặn được đặt thành 1, chuỗi sẽ chặn và đợi 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 yêu cầu.
Thí dụ
#!/usr/bin/python3
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")
Đầu ra
Khi đoạn mã trên được thực thi, nó tạo ra kết quả sau:
Starting Thread-1
Starting Thread-2
Thread-1: Fri Feb 19 10:04:14 2016
Thread-1: Fri Feb 19 10:04:15 2016
Thread-1: Fri Feb 19 10:04:16 2016
Thread-2: Fri Feb 19 10:04:18 2016
Thread-2: Fri Feb 19 10:04:20 2016
Thread-2: Fri Feb 19 10:04:22 2016
Exiting Main Thread
Hàng đợi ưu tiên đa luồng
Các Queue mô-đun cho phép bạn tạo một đối tượng hàng đợi mới có thể tổ chức một số cụ thể của các mặt hàng. Có các phương pháp sau để kiểm soát Hàng đợi:
get() - Hàm get () loại bỏ và trả về một mục từ hàng đợi.
put() - Đặt thêm mục vào hàng đợi.
qsize() - Hàm qsize () trả về số lượng mục hiện đang có trong hàng đợi.
empty()- Giá trị rỗng () trả về giá trị True nếu hàng đợi trống; ngược lại, Sai.
full()- full () trả về True nếu hàng đợi đầy; ngược lại, Sai.
Thí dụ
#!/usr/bin/python3
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")
Đầu ra
Khi đoạn mã trên được thực thi, nó 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