Có khối chờ trong Python không?
Các tác vụ ngốn CPU hoặc các thư viện I/O không đồng bộ có thể chặn vòng lặp sự kiện của chương trình của bạn. Tìm hiểu cách tránh điều này trong Python Show
Lập trình không đồng bộ đã trở thành mô hình tiêu chuẩn cho thiết kế API và hầu hết các dịch vụ. Phạm vi cho bộ kỹ năng của nhà khoa học dữ liệu cũng đã phát triển. Ngày nay không đủ để tạo ra các mô hình hoặc hình ảnh tốt; . Nếu bạn chưa xử lý lập trình không đồng bộ trong quá trình triển khai của mình, rất có thể bạn sẽ sớm Nói rõ hơn, câu chuyện này không phải là một hướng dẫn không đồng bộ khác. Nhưng thay vào đó, một số thông tin chi tiết về những trở ngại phổ biến mà một nhà khoa học dữ liệu có thể gặp phải khi kết nối các công cụ của mình với các khung không đồng bộ. Cụ thể, chặn vòng lặp sự kiện bằng các tác vụ ngốn CPU hoặc thư viện I/O không đồng bộ Câu chuyện này sẽ khám phá cách vòng lặp sự kiện có thể bị chặn và các tài nguyên chúng tôi có để ngăn chặn điều đó Có nhiều thư viện tốt trong Python xử lý lập trình không đồng bộ, nhưng Asyncio phải là thư viện được đưa vào làm tiêu chuẩn trong Python; . Do đó, trong câu chuyện này, chúng tôi sẽ tập trung vào Asyncio Cấu trúc câu chuyện
Vòng lặp sự kiệnCho dù bạn sử dụng mô-đun Asyncio hay bất kỳ thư viện async nào khác, tất cả chúng đều sử dụng vòng lặp sự kiện bên dưới. Vòng lặp sự kiện là một bộ lập lịch chịu trách nhiệm thực thi tất cả các coroutine (chức năng không đồng bộ) trong suốt vòng đời của chương trình Mô hình đồng thời này về cơ bản là một Bây giờ chúng ta đã biết cách thức hoạt động của vòng lặp sự kiện, hãy nghĩ xem điều gì sẽ xảy ra khi chúng ta chạy các tác vụ sử dụng nhiều CPU trong vòng lặp sự kiện. Đây chính xác là nơi cuộc thảo luận trở nên phù hợp với các nhà khoa học dữ liệu. Nếu chúng ta chạy một công việc liên quan đến CPU trong vòng lặp sự kiện, thì vòng lặp đó sẽ chạy tác vụ cho đến khi hoàn thành, giống như bất kỳ vòng lặp Có ba quy tắc khi nói đến vòng lặp sự kiện
Thoạt nhìn, việc chặn vòng lặp sự kiện nghe có vẻ không tệ lắm. Nhưng nghĩ về kịch bản này. Bạn chịu trách nhiệm mã hóa một mô-đun sẽ cung cấp phân tích dữ liệu trong một ứng dụng (dịch vụ) lớn hơn hiển thị API. API được viết trong một khung không đồng bộ. Nếu bạn bọc các chức năng liên quan đến CPU của mình trong các coroutine, bạn có thể sẽ làm tê liệt toàn bộ ứng dụng. Tất cả các tác vụ khác, như xử lý ứng dụng khách, sẽ bị dừng cho đến khi tác vụ ngốn CPU hoàn thành Các phần sau đây sẽ xem xét các cách để chạy các tác vụ chặn vòng lặp sự kiện và nghiên cứu hiệu suất của chúng Thiết lập thử nghiệmChúng tôi bắt đầu thử nghiệm chặn vòng lặp sự kiện với hai chức năng
Hãy dành thời gian (bên ngoài vòng lặp sự kiện) chức năng giới hạn CPU của chúng ta với ma trận vuông gồm 7.000 phần tử mỗi bên để chúng ta biết điều gì sẽ xảy ra
Sau đó, chúng tôi tạo một hàm mô phỏng tác vụ I/O định kỳ. Hàm này chạy một vòng lặp cho Nhật ký thời gian của chức năng này sẽ là dữ liệu của chúng tôi để đánh giá xem các quy trình khác có đang chặn vòng lặp sự kiện hay không. Hơn nữa, chúng tôi định dạng nhật ký thời gian để chúng tôi chỉ giữ lại sự khác biệt về thời gian trước khi tác vụ được thực thi và ngay sau đó. Sử dụng thời gian ngủ là 1 mili giây, đây là giao diện của Gọi chức năng chặn một cách ngây thơCách tiếp cận đầu tiên mà chúng ta có thể thực hiện, một cách ngây thơ, để tạo một thư viện không đồng bộ là bọc các chức năng chặn của chúng ta trong một coroutine Để kiểm tra phương pháp này, chúng tôi gói các chức năng của chúng tôi, giới hạn CPU và chặn luồng ( Bây giờ chúng tôi chạy đồng thời tất cả các tác vụ, Ghi chú. Trong mã này, tôi đã chạy Đây là kết quả của nhật ký thời gian được định dạng Kích thước trục được đo bằng giây [Hình ảnh của tác giả]Như chúng ta có thể thấy từ nhật ký thời gian I/O (ô đồ con đầu tiên), vòng lặp sự kiện bị chặn. Chúng tôi mong đợi mức trung bình là một phần nghìn giây và thời gian thực hiện là hơn 5 giây trong hầu hết các lần lặp lại Hai biểu đồ khác cho thấy các tác vụ khác không thực thi mọi lúc trong quá trình thử nghiệm, thay vào đó cạnh tranh để giành tài nguyên và chặn lẫn nhau Trình thực thi mặc định của AsyncioGiải pháp để tránh tắc nghẽn vòng lặp sự kiện là thực thi mã chặn của chúng tôi ở nơi khác. Chúng ta có thể sử dụng các luồng hoặc các quy trình khác để thực hiện việc này. Asyncio có một phương pháp lặp rất tiện lợi, Đối số đầu tiên của Bao hàm các hàm của chúng ta, tương tự như phần trước và chạy đồng thời các tác vụ Kết quả của các bản ghi thời gian được định dạng là Kích thước trục được đo bằng giây [Hình ảnh của tác giả]Chúng ta có thể thấy rằng vẫn còn một số trục trặc nhỏ trong vòng lặp sự kiện (biểu đồ đầu tiên), nhưng nhật ký thời gian I/O hiển thị chênh lệch thời gian gần một phần nghìn giây. Theo bản thân các tác vụ chặn, chúng đã thực thi đồng thời đồng thời. hợp đồng tương lai ThreadPoolChúng ta có thể tùy chỉnh Sử dụng Thực hiện đồng thời các tác vụ và vẽ biểu đồ nhật ký thời gian được định dạng Kích thước trục được đo bằng giây [Hình ảnh của tác giả]Chúng tôi thấy kết quả tương tự như kết quả thu được trong phần trước; Điều chỉnh số lượng luồng trong nhóm luồng theo nhu cầu của bạn. Kiểm tra số lượng tối ưu là gì. Số lượng luồng cao hơn không phải lúc nào cũng tốt hơn đối với một số trường hợp sử dụng vì nó gây ra một số chi phí Trình thực thi ThreadPool tỏa sáng khi xử lý các thư viện I/O không được viết trong mô hình không đồng bộ. Nhiều thư viện cơ sở dữ liệu trong Python chưa hoạt động với async. Sử dụng chúng trong chương trình không đồng bộ của bạn sẽ chặn vòng lặp sự kiện; đồng thời. hợp đồng tương lai ProcessPoolCuối cùng, chúng ta có thể sử dụng một quy trình riêng để chạy mã chặn của mình. Chúng tôi làm điều này bằng cách chuyển một thể hiện của Chúng tôi tạo lại trình bao bọc định kỳ cho thử nghiệm, hiện đang sử dụng các quy trình riêng biệt Chạy thử nghiệm và vẽ kết quả Kích thước trục được đo bằng giây [Hình ảnh của tác giả]Chúng tôi thấy rằng các trục trặc trong I/O nhật ký thời gian không đáng kể như trong các trường hợp trước. Các quá trình chặn cũng được thực hiện đồng thời Đa xử lý có thể là một giải pháp tốt trong một số trường hợp, đặc biệt là đối với các tác vụ liên quan đến CPU (không phải tác vụ ngủ luồng) mất nhiều thời gian hơn. Tạo nhiều quy trình mới và di chuyển dữ liệu xung quanh rất tốn kém. Tôi. e. , hãy chắc chắn rằng bạn sẵn sàng trả giá cho việc đa xử lý Điểm chuẩn giới hạn I/OBiểu đồ sau đây cho thấy sự khác biệt về thời gian của nhật ký thời gian (càng ít càng tốt) đối với quy trình đăng ký giả I/O (trước và sau khi nhiệm vụ được hoàn thành) bằng cách sử dụng bốn phương pháp được nêu trong các phần trước Kích thước trục được đo bằng giây [Hình ảnh của tác giả]Trong mọi trường hợp, chúng tôi muốn những chênh lệch này gần bằng 1 mili giây vì đó là giá trị lý thuyết. Một số điểm không nhất quán có thể chấp nhận được nhưng không chênh lệch quá 4 giây, như khi chúng tôi chặn vòng lặp sự kiện. Kết quả nhóm luồng (trình thực thi mặc định và ThreadPool với một công nhân) không khác biệt đáng kể. Tuy nhiên, kết quả cho ProcessPool cho thấy rõ ràng rằng trình thực thi này sẽ gây ra sự gián đoạn ít nhất cho vòng lặp sự kiện Điểm chuẩn giới hạn CPUBiểu đồ sau đây cho thấy thời gian cần thiết (càng ít càng tốt) để hoàn thành tác vụ liên quan đến CPU đối với tất cả các phương pháp đã thảo luận trước đó Kích thước trục được đo bằng giây [Hình ảnh của tác giả]Chúng ta có thể thấy rằng việc gọi hàm chặn của chúng ta một cách ngây thơ sẽ mang lại kết quả tốt nhất. Nó có ý nghĩa; . Đối với ba người thi hành khác, kết quả của họ là tương đương nhau; . Di chuyển dữ liệu từ quy trình chính sang quy trình rẽ nhánh mất một thời gian Trong bất kỳ trường hợp nào, chúng tôi có thể nói rằng việc sử dụng một trình thực thi sẽ không dẫn đến hiệu suất quá cao và vòng lặp sự kiện sẽ không bị tắc nghẽn đáng kể. Tiến hành kiểm tra hiệu suất của bạn để chọn người thực thi (và cấu hình) phù hợp. Tuy nhiên, bạn không nên bắt đầu với trình thực thi mặc định và sử dụng nó làm điểm chuẩn Đạo đức của câu chuyệnCho đến nay, chúng ta đã biết rằng việc chặn vòng lặp sự kiện là điều quan trọng chúng ta phải tránh khi thực hiện lập trình không đồng bộ. Nếu bạn quản lý để giữ cho vòng lặp sự kiện không bị tắc nghẽn, mọi thứ sẽ ổn; Đang chờ chặn hay khôngBởi vì await chỉ hợp lệ bên trong các hàm và mô-đun không đồng bộ, bản thân chúng không đồng bộ và trả về lời hứa, nên biểu thức await không bao giờ chặn luồng chính . e. bất cứ điều gì sau biểu thức chờ đợi.
Điều gì xảy ra khi bạn gọi await Python?Từ khóa await chuyển điều khiển chức năng trở lại vòng lặp sự kiện . (Nó tạm dừng việc thực thi coroutine xung quanh. ) Nếu Python gặp một biểu thức await f() trong phạm vi của g() , thì đây là cách await báo cho vòng lặp sự kiện, “Tạm dừng thực thi g() cho đến khi tôi đang đợi bất cứ thứ gì—kết quả của f() —là .
Chờ đợi có chặn dòng mã tiếp theo không?Hiển thị hoạt động trên bài đăng này. Nói tóm lại, chờ đợi chỉ chặn (các) mã sau câu lệnh chờ đợi trong hàm async hiện tại, không phải toàn bộ luồng. Khi sự chờ đợi đã được giải quyết, phần còn lại của mã sẽ được thực thi
Điều gì xảy ra khi sự chờ đợi được gọi?Khi quá trình thực thi đạt đến biểu thức chờ, mã được tạo sẽ kiểm tra xem thứ bạn đang chờ đã có chưa . Nếu có, bạn có thể sử dụng nó và tiếp tục. Nếu không, nó sẽ thêm phần tiếp theo vào phần "đang chờ" và quay lại ngay lập tức. |