Python sleep(): Cách thêm độ trễ vào chương trình phần II (Translated)

Tram Ho

Phần I của bài viết: 

Adding a Python sleep() Call With Decorators

Có những khi bạn cần thử lại một hàm bị lỗi. Một trường hợp phổ biến chính là khi download file lúc server bận. Bạn thường không muốn gửi request lên server một cách quá thường xuyên, vậy nên việc thêm một lời gọi sleep() giữa các request là điều cần thiết.

Một trường hợp khác bản thân tôi đã trải qua chính là khi tôi cần check trạng thái của một UI trong một phiên test tự động. UI có thể load nhanh hơn hoặc chậm hơn bình thường, tùy vào máy tính mà bạn đang chạy test. Điều này có thể thay đổi những gì xuất hiện trên màn hình ở thời điểm chương trình xác thực một điều gì đó.

Trong trường hợp này, tôi có thể để chương trình ngủ một lát và kiểm tra lại mọi thức trong một hoặc hai giây sau đó. Việc chạy test thành công hay thất bại lại nằm ở những khoảnh khắc ngủ này.

Bạn có thể sử dụng decorator để thêm lời gọi sleep() trong những trường hợp này. Nếu bạn không quen với decorator hoặc bạn muốn nâng cao kiến thức về nó, bạn có xem tutorial Primer on Python Decorators. Hãy xem ví dụ sau:

sleep() là decorator của bạn. Nó nhận một giá trị timeout và số lần nó nên retry – mặc định là 3. Trong sleep() là một hàm khác, the_real_decorator(), chấp nhận hàm được decorate.

Cuối cùng, hàm trong cùng wrapper() nhận các đối số (argument) và các đối số từ khóa (keyword argument) mà bạn truyền vào hàm được decorate. Đây là nơi ma thuật xuất hiện! Bạn sử dụng vòng lặp while để thử lại việc gọi hàm. Nếu có exception, bạn gọi time.sleep(), tăng biến đếm retries và chạy hàm lần nữa.

Bây giờ, viết lại uptime_bot() để sử dụng decorator mới của bạn:

Ở đây, bạn decorate uptime_bot() với lệnh sleep() 3 giây. Bạn cũng đã loại bỏ vòng lặp while ban đầu cũng như là lời gọi tới sleep(60). Decorator sẽ đảm nhiệm việc này từ bây giờ.

Một thay đổi khác mà bạn đã thực hiện chính là thêm một lệnh raise bên trong các khối xử lý exception. Việc này đảm bảo decorator sẽ hoạt động đúng đắn. Bạn có thể viết decorator để xử lý những lỗi này nhưng bởi vì các exception chỉ áp dụng với urllib, tốt hơn bạn nên giữ nguyên decorator như trước đó. Như vậy, bạn có thể sử dụng nó ở nhiều nơi.

Có một vài thứ bạn có thể làm để cải tiến decorator của bạn. Nếu chương trình đã thử đủ số lần mà vẫn thất bại, bạn sẽ thấy nó raise lại lỗi cuối cùng. Decorator cũng sẽ chờ 3 giây sau lần thất bại cuối cùng và đây có thể là điều mà bạn không hề mong muốn. Cứ thoải thay đổi nó theo ý bạn nhé!

Adding a Python sleep() Call With Threads

Có những khi bạn muốn thêm lời gọi sleep() vào một thread. Có thể bạn đang chạy một migration script với hàng triệu bản ghi trong production. Bạn không muốn gây ra thời gian chết nhưng cũng không muốn chờ lâu hơn mức cần thiết để hoàn thành việc migration, vậy nên bạn quyết định sử dụng tới các thread.

Chú ý: Thread là một cách thức thực hiện concurrency trong Python. Bạn có thể chạy đồng thời vài thread để tăng hiệu suất chương trình. Nếu bạn chưa quen với thread, bạn có thể xem bài viết An Intro to Threading in Python.

Để tránh việc khách hàng chú ý tới bất cứ sự chậm trễ nào, mỗi thread cần chạy trong một khoảng thời gian ngắn, sau đó đi ngủ. Có hai cách để làm việc này:

  1. Sử dụng time.sleep() như ở trên
  2. Sử dụng Event.wait() từ module threading

Hãy bắt đầu từ time.sleep().

Using time.sleep()

Logging Cookbook đã đưa ra một ví dụ tuyệt vời sử dụng time.sleep(). Module logging có tính thread-safe, vậy nên nó hữu ích hơn một chút so với print() với bài tập này. Đoạn code sau dựa vào ví dụ này:

Ở đây, bạn sử dụng module threading để tạo ra hai thread. Bạn cũng tạo một object logging chịu trách nhiệm log threadName vào stdout. Tiếp theo, bạn khởi động cả hai thread và khởi tạo một vòng lặp từ thread chính liên tục. Bạn sử dụng KeyboardInterrupt để bắt thao tác ^Ctrl + C từ người dùng.

Thử chạy đoạn code trên trong terminal, bạn sẽ thấy output giống như sau:

Khi mỗi thread chạy rồi ngủ, logging sẽ được in ra console. Thông qua ví dụ này, bạn có thể sử dụng những khái niệm này trong code của riêng bạn.

Using Event.wait()

Module threading cung cấp Event() mà bạn có thể sử dụng như time.sleep(). Tuy nhiên, Event() có ưu điểm là nó đáp ứng tốt hơn. Lý giải cho điều này là khi event được thiết lập, chương trình sẽ thoát ra khỏi vòng loop ngay lập tức. Với time.sleep(), code của bạn sẽ cần chờ lời gọi sleep() kết thúc trước khi thread có thể thoát.

Lý do bạn muốn sử dụng wait() ở đây là bởi vì wait() có tính non-blocking trong khi time.sleep()blocking. Điều này có nghĩ là khi bạn sử dụng time.sleep(), bạn sẽ chặn thread chính và nó sẽ không tiếp tục chạy trong khi chờ lời gọi sleep() kết thúc. wait() giải tuyết được vấn đề này. Bạn có thể đọc thêm về tất cả những thứ liên quan đến threading ở phần documentation của Python.

Đây là cách bạn thêm một lời gọi sleep() với Event.wait():

Trong ví dụ này, bạn tạo ra threading.Event() và truyền nó cho worker(). (Nhớ lại trong ví dụ trước, bạn đã truyền một dict). Tiếp theo, bạn có thể dựng lên các vòng lặp để kiểm tra event đã được thiết lập hay chưa. Nếu chưa, code của bạn sẽ in ra một message và chờ một chút trước khi kiểm tra lại. Để thiết lập event, bạn có ấn ^Ctrl + C. Một khi event đã được thiết lập, worker() sẽ trả về, vòng loop sẽ bị break và kết thúc chương trình.

Nguồn: https://realpython.com/python-sleep/

Chia sẻ bài viết ngay

Nguồn bài viết : Viblo