Tóm tắt: trong hướng dẫn này, bạn sẽ học cách sử dụng mô-đun thread Python để phát triển các ứng dụng đa thread.
Các bài viết liên quan:
Ứng dụng Single-threaded
Hãy bắt đầu với một chương trình đơn giản:
#khai báo thư viện time và sleep, perf_counter from time import sleep, perf_counter #khai báo hàm task() def task(): print('bắt đầu nhiệm vụ...') sleep(1) print('hoàn thành') #gọi hàm stat time đếm giờ start_time = perf_counter() task() task() #gọi hàm end time đếm giờ end_time = perf_counter() print(f'thời gian hoàn thành {end_time- start_time: 0.2f} second(s).')
Làm thế nào nó hoạt động.
- Đầu tiên, nhập các hàm sleep() và perf_counter() từ mô-đun thời gian:
- Thứ hai, xác định một hàm mất một giây để hoàn thành:
- Thứ ba, lấy giá trị của bộ đếm hiệu năng bằng cách gọi hàm perf_counter():
- Thứ tư, gọi hàm task() hai lần:
- 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():
- Cuối cùng, xuất ra thời gian cần thiết để hoàn thành việc chạy hàm task() hai lần:
Như bạn có thể thấy, 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, sẽ mất khoảng 10 giây để hoàn thành.
Sơ đồ sau đây minh họa cách hoạt động của chương trình:
Đầu tiên, hàm task() thực thi và sleep trong một giây. Sau đó, nó thực thi lần thứ hai và cũng sleep thêm một giây nữa. Cuối cùng, chương trình hoàn thành.
Khi hàm task() gọi hàm sleep(), CPU không hoạt động. Nói cách khác, CPU không làm gì cả, điều này không hiệu quả về mặt sử dụng tài nguyên.
Chương trình này có một tiến trình với một thread duy nhất, được gọi là thread chính. Vì chương trình chỉ có một thread nên nó được gọi là chương trình đơn thread.
Sử dụng thread Python để phát triển một ví dụ về chương trình đa thread
Để tạo một chương trình đa thread, bạn cần sử dụng mô-đun thread Python.
Đầu tiên, nhập lớp Thread từ mô-đun thread:
from threading import Thread
Thứ hai, tạo một thread mới bằng cách khởi tạo một thể hiện của lớp Thread:
new_thread = Thread(target=fn,args=args_tuple)
Thread() chấp nhận nhiều tham số. Những cái chính là:
- target: chỉ định một chức năng (fn) để chạy trong thread mới.
- args: chỉ định các đối số của hàm (fn). Đối số args là một bộ.
Thứ ba, khởi động thread bằng cách gọi phương thức start() của thể hiện thread:
new_thread.start()
Nếu bạn muốn đợi thread hoàn thành trong main thread, 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(), thread chính sẽ đợi thread thứ hai hoàn thành trước khi nó kết thúc.
Chương trình sau đây minh họa cách sử dụng mô-đun phân thread:
from time import sleep, perf_counter from threading import Thread def task(): print('bắt đầu task...') sleep(1) print('hoàn thành') start_time = perf_counter() # tạo hai thread mới t1 = Thread(target=task) t2 = Thread(target=task) # băt đầu chạy threads t1.start() t2.start() # đợi thread hoàn thành t1.join() t2.join() end_time = perf_counter() print(f'thời gian hoàn thành {end_time- start_time: 0.2f} second(s).')
Làm thế nào nó hoạt động. (và chúng tôi sẽ chỉ tập trung vào phần thread)
Đầu tiên, tạo hai thread mới:
t1 = Thread(target=task) t2 = Thread(target=task)
Thứ hai, bắt đầu cả hai thread bằng cách gọi phương thức start():
t1.start() t2.start()
Thứ ba, đợi cả hai thread hoàn thành:
t1.join() t2.join()
Cuối cùng, hiển thị thời gian thực hiện:
print(f'thời gian hoàn thành {end_time- start_time: 0.2f} second(s).')
Khi chương trình thực thi, nó sẽ có ba thread: thread chính được tạo bởi trình thông dịch Python và hai thread được tạo bởi chương trình.
Như được hiển thị rõ ràng từ đầu ra, chương trình mất một giây thay vì hai giây để hoàn thành.
Sơ đồ sau đây cho thấy cách các thread thực thi:
Truyền đối số cho thread
Chương trình sau đây chỉ ra cách truyền đối số cho hàm được gán cho một thread:
from time import sleep, perf_counter from threading import Thread def task(id): print(f'bắt đầu chạy task {id}...') sleep(1) print(f'task có id {id} hoàn thành') start_time = perf_counter() # tạo và chạy 10 threads threads = [] for n in range(1, 11): t = Thread(target=task, args=(n,)) threads.append(t) t.start() # đợi thread hoàn thành for t in threads: t.join() end_time = perf_counter() print(f'thời gian hoàn thành {end_time- start_time: 0.2f} second(s).')
Làm thế nào nó hoạt động.
Đầu tiên, xác định một hàm task() chấp nhận một đối số:
def task(id): print(f'bắt đầu chạy task {id}...') sleep(1) print(f'task có id {id} hoàn thành')
Thứ hai, tạo 10 thread mới và chuyển id cho mỗi thread. Danh sách thread được sử dụng để theo dõi tất cả các thread mới được tạo:
# tạo và chạy 10 threads threads = [] for n in range(1, 11): t = Thread(target=task, args=(n,)) threads.append(t) t.start()
Lưu ý rằng nếu bạn gọi phương thức join() bên trong vòng lặp, chương trình sẽ đợi thread đầu tiên hoàn thành trước khi bắt đầu thread tiếp theo.
Thứ ba, đợi tất cả các thread hoàn thành bằng cách gọi phương thức join():
for t in threads: t.join()
bắt đầu chạy task 1... bắt đầu chạy task 2... bắt đầu chạy task 3... bắt đầu chạy task 4... bắt đầu chạy task 5... bắt đầu chạy task 6... bắt đầu chạy task 7... bắt đầu chạy task 8... bắt đầu chạy task 9... bắt đầu chạy task 10... task có id 1 hoàn thành task có id 3 hoàn thành task có id 2 hoàn thành task có id 4 hoàn thành task có id 5 hoàn thành task có id 7 hoàn thành task có id 6 hoàn thành task có id 9 hoàn thành task có id 10 hoàn thành task có id 8 hoàn thành thời gian hoàn thành 1.00 second(s). >
Nó chỉ mất 1,00 giây để hoàn thành.
Lưu ý rằng chương trình không thực hiện thread theo thứ tự từ 1 đến 10.
Khi nào nên sử dụng thread Python
Như đã giới thiệu trong hướng dẫn quy trình và thread, có hai nhiệm vụ chính:
- Các tác vụ liên quan đến I/O – 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
- Các tác vụ liên quan đến CPU – thời gian dành cho tính toán cao hơn đáng kể so với thời gian chờ I/O.
Phân thread Python được tối ưu hóa cho các tác vụ ràng buộc I/O. 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 tệp.
Một ví dụ thread Python thực tế
Giả sử bạn có một danh sách các tệp văn bản trong một thư mục, ví dụ: C:/temp/. Và bạn muốn thay thế một văn bản bằng một văn bản mới trong tất cả các tệp.
Chương trình đơn thread sau đây cho biết cách thay thế chuỗi con bằng chuỗi con mới trong tệp văn bản:
from time import perf_counter def replace(filename, substr, new_substr): print(f'Processing tiến hành file {filename}') # lấy nội dung của tập tin with open(filename, 'r') as f: content = f.read() # thay chất nền bằng new_substr content = content.replace(substr, new_substr) # ghi dữ liệu vào tệp with open(filename, 'w') as f: f.write(content) def main(): #list filenames 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', ] #duyệt vòng for for filename in filenames: replace(filename, 'ids', 'id') if __name__ == "__main__": start_time = perf_counter() main() end_time = perf_counter() print(f'thời gian hoàn thành {end_time- start_time :0.2f} second(s).')
Chương trình sau đây có chức năng tương tự. Tuy nhiên, nó sử dụng nhiều thread thay thế:
from threading import Thread from time import perf_counter def replace(filename, substr, new_substr): print(f'Processing the file {filename}') # lấy nội dung của tập tin with open(filename, 'r') as f: content = f.read() # thay chất nền bằng new_substr content = content.replace(substr, new_substr) # ghi dữ liệu vào tệp 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 chủ đề threads = [Thread(target=replace, args=(filename, 'id', 'ids')) for filename in filenames] # băt đầu threads for thread in threads: thread.start() # đợi tiến trình complete for thread in threads: thread.join() if __name__ == "__main__": start_time = perf_counter() main() end_time = perf_counter() print(f'thời gian hoàn thành {end_time- start_time :0.2f} second(s).')
Như bạn có thể thấy rõ từ đầu ra, chương trình đa thread chạy nhanh hơn rất nhiều.
Tóm lược
Sử dụng mô-đun thread Python để tạo ứng dụng đa thread.
Sử dụng Thread(function, args) để tạo một thread mới.
Gọi phương thức start() của lớp Thread để bắt đầu thread.
Gọi phương thức join() của lớp Thread để đợi thread hoàn thành trong thread chính.
Chỉ sử dụng phân thread cho các ứng dụng xử lý liên kết I/O.