Python - Multithreading

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.

Comparison with Processes

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.

multithreading

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.

Thread Handling Modules in Python

Thư viện chuẩn của Python cung cấp hai mô-đun chính để quản lý luồng: _thread threading .

The _thread Module

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.

The threading Module

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.

  • threading.activeCount() − Returns the number of thread objects that are active.
  • threading.currentThread() − Returns the number of thread objects in the caller's thread control.
  • threading.enumerate() − Returns a list of all thread objects that are currently active.

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 −

  • run() − The run() method is the entry point for a thread.
  • start() − The start() method starts a thread by calling the run method.
  • join([time]) − The join() waits for threads to terminate.
  • isAlive() − The isAlive() method checks whether a thread is still executing.
  • getName() − The getName() method returns the name of a thread.
  • setName() − The setName() method sets the name of a thread.

Starting a New Thread

Để 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.

Starting a New Thread Using the _thread Module

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.

Starting a New Thread Using the Threading Module

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 −

  • Create a function that you want the thread to execute.
  • Then create a Thread object using the Thread class by passing the target function and its arguments.
  • Call the start method on the Thread object to begin execution.
  • Optionally, call the join method to wait for the thread to complete before proceeding.

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

Synchronizing Threads

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.

Example

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

Multithreaded Priority Queue

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.

Example

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