Thông báo: Download 4 khóa học Python từ cơ bản đến nâng cao tại đây.
Cách sử dụng module threading trong Python
Việc tối ưu hóa hiệu suất và sử dụng tài nguyên một cách hiệu quả là vô cùng quan trọng. Một trong những cách để đạt được điều này là sử dụng đa luồng. Trong Python, module threading
cung cấp các công cụ mạnh mẽ để tạo và quản lý các luồng, giúp các ứng dụng của bạn chạy mượt mà và nhanh chóng hơn, đặc biệt là đối với các tác vụ I/O-bound. Hướng dẫn này sẽ giúp bạn hiểu rõ hơn về cách sử dụng module threading
trong Python để phát triển các ứng dụng đa luồng, từ đó nâng cao hiệu suất và khả năng xử lý của chương trình.
Ứng dụng đơn luồng trong Python
Hãy bắt đầu với một chương trình đơn giản:
from time import sleep, perf_counter def task(): print('Starting a task...') sleep(1) print('done') start_time = perf_counter() task() task() end_time = perf_counter() print(f'It took {end_time- start_time: 0.2f} second(s) to complete.')
Cách hoạt động của chương trình:
Đầu tiên, import các hàm sleep()
và perf_counter()
từ module time
:
Bài viết này được đăng tại [free tuts .net]
from time import sleep, perf_counter
Thứ hai, định nghĩa một hàm mất 1 giây để hoàn thành:
def task(): print('Starting a task...') sleep(1) print('done')
Thứ ba, lấy giá trị của bộ đếm hiệu năng bằng cách gọi hàm perf_counter()
:
start_time = perf_counter()
Thứ tư, gọi hàm task()
hai lần:
task() task()
Thứ năm, lấy giá trị của bộ đếm hiệu năng bằng cách gọi hàm perf_counter()
:
end_time = perf_counter()
Cuối cùng, hiển thị thời gian hoàn thành của việc chạy hàm task()
hai lần:
print(f'It took {end_time- start_time: 0.2f} second(s) to complete.')
Dưới đây là kết quả:
Starting a task... done Starting a task... done It took 2.00 second(s) to complete.
Chương trình mất khoảng hai giây để hoàn thành. Nếu bạn gọi hàm task()
10 lần, nó sẽ mất khoảng 10 giây để hoàn thành.
Chương trình này có một tiến trình với một luồng duy nhất, gọi là luồng chính (main thread). Vì chương trình chỉ có một luồng, nó được gọi là chương trình đơn luồng.
Sử dụng threading để phát triển chương trình đa luồng Python
Để tạo một chương trình đa luồng, bạn cần sử dụng module threading trong Python.
Đầu tiên, import lớp Thread
từ module threading:
from threading import Thread
Thứ hai, tạo một luồng mới bằng cách khởi tạo một đối tượng Thread
:
new_thread = Thread(target=fn, args=args_tuple)
Thread()
chấp nhận nhiều tham số. Những tham số chính là:
target
: xác định hàm (fn) để chạy trong luồng mới.args
: xác định các tham số của hàm (fn).args
là một tuple.
Thứ ba, khởi động luồng bằng cách gọi phương thức start()
của đối tượng Thread
:
new_thread.start()
Nếu bạn muốn chờ luồng hoàn thành trong luồng chính, bạn có thể gọi phương thức join()
:
new_thread.join()
Bằng cách gọi phương thức join()
, luồng chính sẽ chờ luồng con hoàn thành trước khi nó kết thúc.
Dưới đây là chương trình minh họa cách sử dụng module threading:
from time import sleep, perf_counter from threading import Thread def task(): print('Starting a task...') sleep(1) print('done') start_time = perf_counter() # tạo hai luồng mới t1 = Thread(target=task) t2 = Thread(target=task) # khởi động các luồng t1.start() t2.start() # chờ các luồng hoàn thành t1.join() t2.join() end_time = perf_counter() print(f'It took {end_time- start_time: 0.2f} second(s) to complete.')
Cách hoạt động của chương trình:
Tạo hai luồng mới:
t1 = Thread(target=task) t2 = Thread(target=task)
Khởi động cả hai luồng bằng cách gọi phương thức start()
:
t1.start() t2.start()
Chờ cả hai luồng hoàn thành bằng cách gọi phương thức join()
:
t1.join() t2.join()
Cuối cùng, hiển thị thời gian thực thi:
print(f'It took {end_time- start_time: 0.2f} second(s) to complete.')
Kết quả:
Starting a task... Starting a task... done done It took 1.00 second(s) to complete.
Chương trình sẽ có ba luồng: luồng chính và hai luồng con khác. Từ kết quả, chương trình mất một giây thay vì hai giây để hoàn thành.
Truyền tham số cho các luồng trong Python
Chương trình sau đây minh họa cách truyền tham số cho hàm được gán cho một luồng:
from time import sleep, perf_counter from threading import Thread def task(id): print(f'Starting the task {id}...') sleep(1) print(f'The task {id} completed') start_time = perf_counter() # tạo và khởi động 10 luồng threads = [] for n in range(1, 11): t = Thread(target=task, args=(n,)) threads.append(t) t.start() # chờ các luồng hoàn thành for t in threads: t.join() end_time = perf_counter() print(f'It took {end_time- start_time: 0.2f} second(s) to complete.')
Cách hoạt động của chương trình:
Định nghĩa một hàm task()
nhận một tham số:
def task(id): print(f'Starting the task {id}...') sleep(1) print(f'The task {id} completed')
Tạo 10 luồng mới và truyền một id
cho mỗi luồng. Danh sách threads
được sử dụng để theo dõi tất cả các luồng mới tạo:
threads = [] for n in range(1, 11): t = Thread(target=task, args=(n,)) threads.append(t) t.start()
Chờ tất cả các luồng hoàn thành bằng cách gọi phương thức join()
:
for t in threads: t.join()
Kết quả:
Starting the task 1... Starting the task 2... Starting the task 3... Starting the task 4... Starting the task 5... Starting the task 6... Starting the task 7... Starting the task 8... Starting the task 9... Starting the task 10... The task 10 completed The task 8 completed The task 1 completed The task 6 completed The task 7 completed The task 9 completed The task 3 completed The task 4 completed The task 2 completed The task 5 completed It took 1.02 second(s) to complete.
Chương trình chỉ mất khoảng 1,02 giây để hoàn thành. Lưu ý rằng chương trình không thực thi các luồng theo thứ tự từ 1 đến 10.
Khi nào nên sử dụng threading trong Python?
Như đã giới thiệu trong hướng dẫn về tiến trình và luồng, có hai loại tác vụ chính:
- Tác vụ I/O-bound: thời gian dành cho I/O nhiều hơn đáng kể so với thời gian dành cho tính toán.
- Tác vụ CPU-bound: thời gian dành cho tính toán nhiều hơn đáng kể so với thời gian chờ I/O.
Threading trong Python được tối ưu hóa cho các tác vụ I/O-bound. Ví dụ, yêu cầu tài nguyên từ xa, kết nối máy chủ cơ sở dữ liệu hoặc đọc và ghi file.
Ví dụ thực tế về threading trong Python
Giả sử bạn có một danh sách các file văn bản trong một thư mục, ví dụ: C:/temp/
. Và bạn muốn thay thế một đoạn văn bản bằng một đoạn mới trong tất cả các file.
Chương trình đơn luồng dưới đây cho thấy cách thay thế một chuỗi con bằng chuỗi mới trong các file văn bản:
from time import perf_counter def replace(filename, substr, new_substr): print(f'Processing the file {filename}') # đọc nội dung file with open(filename, 'r') as f: content = f.read() # thay thế chuỗi con content = content.replace(substr, new_substr) # ghi dữ liệu vào file with open(filename, 'w') as f: f.write(content) def main(): filenames = [ 'c:/temp/test1.txt', 'c:/temp/test2.txt', 'c:/temp/test3.txt', 'c:/temp/test4.txt', 'c:/temp/test5.txt', 'c:/temp/test6.txt', 'c:/temp/test7.txt', 'c:/temp/test8.txt', 'c:/temp/test9.txt', 'c:/temp/test10.txt', ] for filename in filenames: replace(filename, 'ids', 'id') if __name__ == "__main__": start_time = perf_counter() main() end_time = perf_counter() print(f'It took {end_time- start_time :0.2f} second(s) to complete.')
Kết quả:
It took 0.16 second(s) to complete.
Chương trình đa luồng có cùng chức năng như sau:
from threading import Thread from time import perf_counter def replace(filename, substr, new_substr): print(f'Processing the file {filename}') # đọc nội dung file with open(filename, 'r') as f: content = f.read() # thay thế chuỗi con content = content.replace(substr, new_substr) # ghi dữ liệu vào file with open(filename, 'w') as f: f.write(content) def main(): filenames = [ 'c:/temp/test1.txt', 'c:/temp/test2.txt', 'c:/temp/test3.txt', 'c:/temp/test4.txt', 'c:/temp/test5.txt', 'c:/temp/test6.txt', 'c:/temp/test7.txt', 'c:/temp/test8.txt', 'c:/temp/test9.txt', 'c:/temp/test10.txt', ] # tạo các luồng threads = [Thread(target=replace, args=(filename, 'id', 'ids')) for filename in filenames] # khởi động các luồng for thread in threads: thread.start() # chờ các luồng hoàn thành for thread in threads: thread.join() if __name__ == "__main__": start_time = perf_counter() main() end_time = perf_counter() print(f'It took {end_time- start_time :0.2f} second(s) to complete.')
Kết quả:
Processing the file c:/temp/test1.txt Processing the file c:/temp/test2.txt Processing the file c:/temp/test3.txt Processing the file c:/temp/test4.txt Processing the file c:/temp/test5.txt Processing the file c:/temp/test6.txt Processing the file c:/temp/test7.txt Processing the file c:/temp/test8.txt Processing the file c:/temp/test9.txt Processing the file c:/temp/test10.txt It took 0.02 second(s) to complete.
Chương trình đa luồng chạy nhanh hơn nhiều.
Kết bài
Việc sử dụng module threading
trong Python mang lại nhiều lợi ích đáng kể khi phát triển các ứng dụng đa luồng, đặc biệt là trong các tác vụ I/O-bound. Bằng cách tạo các luồng mới thông qua Thread(function, args)
, khởi động chúng bằng phương thức start()
, và chờ đợi chúng hoàn thành với phương thức join()
, bạn có thể tối ưu hóa hiệu suất và sử dụng tài nguyên hệ thống một cách hiệu quả hơn. Tuy nhiên, cần lưu ý rằng đa luồng chỉ thực sự hữu ích với các tác vụ I/O-bound, vì các tác vụ CPU-bound thường không mang lại lợi ích tương tự do những hạn chế của GIL (Global Interpreter Lock) trong Python. Hy vọng rằng qua hướng dẫn này, bạn đã nắm bắt được cách sử dụng threading
để phát triển các ứng dụng đa luồng trong Python, giúp tối ưu hóa và cải thiện hiệu suất chương trình của mình.