PYTHON CONCURRENCY
CÁC CHỦ ĐỀ
BÀI MỚI NHẤT
MỚI CẬP NHẬT

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.

test php

banquyen png
Bài viết này được đăng tại freetuts.net, không được copy dưới mọi hình thức.

Ứ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()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.

Python Closure Example png

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.

Python Threading Multi threaded App png

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.

Cùng chuyên mục:

Sử dụng câu lệnh raise from trong Python

Sử dụng câu lệnh raise from trong Python

Ngoại lệ tùy chỉnh trong Python

Ngoại lệ tùy chỉnh trong Python

Ngoại lệ Raise trong Python

Ngoại lệ Raise trong Python

Tìm hiểu về các ngoại lệ trong Python

Tìm hiểu về các ngoại lệ trong Python

Tìm hiểu về decorator dataclass trong Python

Tìm hiểu về decorator dataclass trong Python

Ví dụ sử dụng metaclass trong Python

Ví dụ sử dụng metaclass trong Python

Lớp Metaclass trong Python

Lớp Metaclass trong Python

Tìm hiểu về Class Type trong Python

Tìm hiểu về Class Type trong Python

Phương thức __new__ trong Python

Phương thức __new__ trong Python

Phân biệt Data Descriptor và Non-data Descriptor trong Python

Phân biệt Data Descriptor và Non-data Descriptor trong Python

Mô tả Descriptors trong Python

Mô tả Descriptors trong Python

Tìm hiểu về các lớp mixin trong Python

Tìm hiểu về các lớp mixin trong Python

Đa kế thừa trong Python

Đa kế thừa trong Python

Nguyên tắc đảo ngược sự phụ thuộc trong Python

Nguyên tắc đảo ngược sự phụ thuộc trong Python

Interface Segregation Principle - ISP trong Python.

Interface Segregation Principle - ISP trong Python.

Nguyên tắc thay thế Liskov - LSP trong Python

Nguyên tắc thay thế Liskov - LSP trong Python

Nguyên tắc Đóng-Mở trong Python

Nguyên tắc Đóng-Mở trong Python

Single Responsibility Principle trong Python

Single Responsibility Principle trong Python

Cách sử dụng hàm Auto() của Python

Cách sử dụng hàm Auto() của Python

Tùy chỉnh và mở rộng lớp Enum trong Python

Tùy chỉnh và mở rộng lớp Enum trong Python

Top