Cách khóa mã Python
Bài viết này nhằm mục đích làm thế nào để khóa các luồng và các phần quan trọng trong chương trình nhất định để tránh các điều kiện chạy đua. Vì vậy, sử dụng đối tượng Khóa trong thư viện luồng để làm cho các đối tượng có thể thay đổi được sử dụng an toàn bởi nhiều luồng Show Các hướng dẫn được cung cấp mô tả cách khóa tập lệnh Python để không thể chỉnh sửa tập lệnh đó. Khi tập lệnh sẽ được chia sẻ nhưng mã cần không thay đổi, hãy biên dịch tệp PY thành tệp PYC. Điều này đảm bảo rằng người dùng không thể thực hiện bất kỳ thay đổi nào đối với mã hiện có trong tập lệnh. Tệp PYC có bản chất là nhị phân và không thể đọc trực tiếp Thủ tụcDưới đây là mã biên dịch tệp PY thành tệp PYC Note: Code: Mã này có thể được nhập dưới dạng tệp PY của chính nó hoặc có thể được nhập vào Cửa sổ ngay lập tức của PythonWin Trong chương này, chúng ta sẽ tìm hiểu cách kiểm soát quyền truy cập vào các tài nguyên được chia sẻ. Việc kiểm soát là cần thiết để ngăn chặn tham nhũng của dữ liệu. Nói cách khác, để bảo vệ chống truy cập đồng thời vào một đối tượng, chúng ta cần sử dụng một đối tượng Khóa Khóa nguyên thủy là nguyên thủy đồng bộ hóa không thuộc sở hữu của một luồng cụ thể khi bị khóa. Trong Python, nó hiện là nguyên mẫu đồng bộ hóa mức thấp nhất hiện có, được triển khai trực tiếp bởi mô-đun mở rộng _thread. Khóa nguyên thủy ở một trong hai trạng thái, "đã khóa" hoặc "đã mở khóa". Nó được tạo ở trạng thái mở khóa. Nó có hai phương thức cơ bản, thu được() và phát hành(). Khi trạng thái được mở khóa, thu được () sẽ thay đổi trạng thái thành bị khóa và quay lại ngay lập tức. Khi trạng thái bị khóa, lệnh thu thập () sẽ chặn cho đến khi lệnh gọi giải phóng () trong một chuỗi khác thay đổi trạng thái đó thành mở khóa, sau đó lệnh gọi thu thập () đặt lại thành bị khóa và trả về. Phương thức release() chỉ nên được gọi ở trạng thái bị khóa; . Nếu một nỗ lực được thực hiện để giải phóng một khóa đã mở khóa, RuntimeError sẽ được nâng lên Đây là mã ví dụ của chúng tôi bằng cách sử dụng đối tượng Lock. Trong mã, hàm worker() tăng một phiên bản Bộ đếm, quản lý Khóa để ngăn hai luồng thay đổi trạng thái bên trong của nó cùng một lúc import threading import time import logging import random logging.basicConfig(level=logging.DEBUG, format='(%(threadName)-9s) %(message)s',) class Counter(object): def __init__(self, start = 0): self.lock = threading.Lock() self.value = start def increment(self): logging.debug('Waiting for a lock') self.lock.acquire() try: logging.debug('Acquired a lock') self.value = self.value + 1 finally: logging.debug('Released a lock') self.lock.release() def worker(c): for i in range(2): r = random.random() logging.debug('Sleeping %0.02f', r) time.sleep(r) c.increment() logging.debug('Done') if __name__ == '__main__': counter = Counter() for i in range(2): t = threading.Thread(target=worker, args=(counter,)) t.start() logging.debug('Waiting for worker threads') main_thread = threading.currentThread() for t in threading.enumerate(): if t is not main_thread: t.join() logging.debug('Counter: %d', counter.value) đầu ra (Thread-1 ) Sleeping 0.04 (MainThread) Waiting for worker threads (Thread-2 ) Sleeping 0.11 (Thread-1 ) Waiting for a lock (Thread-1 ) Acquired a lock (Thread-1 ) Released a lock (Thread-1 ) Sleeping 0.30 (Thread-2 ) Waiting for a lock (Thread-2 ) Acquired a lock (Thread-2 ) Released a lock (Thread-2 ) Sleeping 0.27 (Thread-1 ) Waiting for a lock (Thread-1 ) Acquired a lock (Thread-1 ) Released a lock (Thread-1 ) Done (Thread-2 ) Waiting for a lock (Thread-2 ) Acquired a lock (Thread-2 ) Released a lock (Thread-2 ) Done (MainThread) Counter: 4 Trong ví dụ này, worker() cố gắng lấy khóa ba lần riêng biệt và đếm xem nó phải thực hiện bao nhiêu lần để làm như vậy. Đồng thời, tủ khóa () xoay vòng giữa giữ và nhả khóa, với thời gian ngủ ngắn ở mỗi trạng thái được sử dụng để mô phỏng tải Mỗi chương trình Python có ít nhất một luồng thực thi được gọi là luồng chính. Cả quy trình và luồng đều được tạo và quản lý bởi hệ điều hành bên dưới Đôi khi, chúng tôi có thể cần tạo các luồng bổ sung trong chương trình của mình để thực thi mã đồng thời Python cung cấp khả năng tạo và quản lý các luồng mới thông qua mô-đun luồng và luồng. lớp chủ đề Bạn có thể tìm hiểu thêm về Python thread trong hướng dẫn
Trong lập trình đồng thời, chúng ta có thể cần khóa một hàm để bảo vệ nó khỏi bị gọi đồng thời từ nhiều luồng Làm cách nào chúng ta có thể khóa một hàm trong Python? Chạy các vòng lặp của bạn bằng cách sử dụng tất cả các CPU, tải xuống cuốn sách MIỄN PHÍ của tôi để tìm hiểu cách thực hiện Cách khóa một chức năngMột chức năng có thể bị khóa bằng cách sử dụng khóa loại trừ lẫn nhau (mutex) Khóa MutexKhóa loại trừ lẫn nhau hoặc khóa mutex là nguyên tắc đồng bộ hóa nhằm ngăn chặn tình trạng dồn đuổi Khóa mutex có thể được sử dụng để đảm bảo rằng mỗi lần chỉ có một luồng thực thi đoạn mã quan trọng, trong khi tất cả các luồng khác đang cố thực thi cùng một mã phải đợi cho đến khi luồng đang thực thi kết thúc với phần quan trọng và phát hành Python cung cấp khóa loại trừ lẫn nhau thông qua luồng. khóa lớp Đầu tiên, một phiên bản của khóa phải được tạo 1 2 3 .. . # tạo khóa khóa = Khóa() Sau đó, khóa có thể được sử dụng để bảo vệ các phần quan trọng của mã yêu cầu quyền truy cập theo thứ tự hoặc loại trừ lẫn nhau Điều này có thể đạt được bằng cách lấy khóa trước bằng cách gọi hàm thu thập () trước khi truy cập phần quan trọng, sau đó giải phóng khóa bằng cách gọi giải phóng () sau phần quan trọng Ví dụ 1 2 3 4 5 6 7 .. . # lấy khóa khóa. có được() # phần quan trọng # # nhả khóa khóa. phát hành() Chỉ một chủ đề có thể có khóa bất cứ lúc nào Nếu khóa đang được giữ, một luồng khác đang cố lấy khóa sẽ chặn cho đến khi khóa khả dụng Chúng tôi cũng có thể sử dụng khóa thông qua giao diện trình quản lý bối cảnh Điều này sẽ cho phép phần quan trọng trở thành một khối trong quá trình sử dụng khóa và khóa sẽ tự động được giải phóng sau khi khối hoàn thành Ví dụ 1 2 3 4 .. . # lấy khóa với khóa. #. Trình quản lý ngữ cảnh là cách tiếp cận ưa thích để sử dụng khóa vì nó đảm bảo khóa luôn được giải phóng, ngay cả khi Lỗi hoặc Ngoại lệ không mong muốn xuất hiện Bạn có thể tìm hiểu thêm về khóa mutex trong hướng dẫn
Khóa chức năngCó hai cách để khóa một chức năng họ đang
Việc lựa chọn cách tiếp cận phụ thuộc vào các chi tiết của ứng dụng của bạn Ví dụ: nếu bạn có thể thay đổi chức năng đích, thì có lẽ nên ưu tiên quản lý khóa trong chức năng đó Ngoài ra, nếu chức năng chỉ được gọi một hoặc một vài lần hoặc bạn không có quyền kiểm soát chức năng đích, thì việc kiểm soát khóa trong trình gọi chức năng sẽ được ưu tiên Hãy xem xét kỹ hơn từng phương pháp Chức năng khóa trong người gọiMột chức năng có thể bị khóa bởi người gọi chức năng Người gọi trước tiên lấy khóa, sau đó gọi hàm, sau đó nhả khóa sau khi hàm trả về
Điều này có thể đạt được bằng cách sử dụng khóa thông qua giao diện trình quản lý bối cảnh trên luồng. Khóa Ví dụ 1 2 3 4 5 .. . # lấy khóa với khóa. #gọi hàm công việc() Một lợi ích của phương pháp này là chức năng tự nó không yêu cầu bất kỳ thay đổi nào. Nhược điểm của phương pháp này là tất cả các lệnh gọi hàm phải được cập nhật để sử dụng khóa và tất cả những người gọi phải chia sẻ cùng một trường hợp khóa Như vậy, nó đòi hỏi bạn
Chức năng khóa nội bộChức năng có thể bị khóa trực tiếp trong chính chức năng đó Điều này yêu cầu thay đổi chức năng để lấy khóa là hành động đầu tiên của chức năng và giải phóng khóa là hành động cuối cùng của chức năng Điều này có thể đạt được dễ dàng bằng cách sử dụng giao diện quản lý ngữ cảnh trên luồng. Khóa Ví dụ 1 2 3 4 5 6 7 # chức năng tùy chỉnh def hoạt động(). # khai báo phạm vi của biến khóa toàn cục được xác định trước đó khóa toàn cầu # lấy khóa với khóa. .. . Nó yêu cầu cùng một khóa luôn được sử dụng trong chức năng Điều này có thể đạt được bằng cách
Một lợi ích của phương pháp này là nó không yêu cầu bất kỳ thay đổi nào đối với người gọi hàm, nhưng yêu cầu phải cung cấp khóa, có khả năng thêm một đối số vào hàm hoặc một biến toàn cục bổ sung Bây giờ chúng ta đã biết cách khóa một chức năng, hãy xem một số ví dụ đã hoạt động Bối rối với API mô-đun phân luồng? Ví dụ về Khóa chức năngChúng ta có thể khám phá cách khóa một chức năng từ bên trong chính chức năng đó Khóa phải được chia sẻ giữa mỗi cuộc gọi đến chức năng Hai cách để đạt được điều này là
Hãy xem xét một ví dụ về từng cách tiếp cận Chức năng khóa với đối sốChúng ta có thể khóa một chức năng từ bên trong nơi khóa được cung cấp bởi một đối số Đầu tiên, chúng ta có thể định nghĩa một hàm lấy khóa chia sẻ làm đối số, lấy khóa sau đó thực hiện nhiệm vụ của hàm Hàm task() bên dưới thực hiện điều này 1 2 3 4 5 6 # chức năng được gọi trong một chủ đề mới def nhiệm vụ(khóa): # giành được khóa với khóa. # chặn trong giây lát ngủ(1) Chức năng này sẽ được thực hiện trong một chủ đề mới Trong luồng chính, trước tiên chúng ta có thể tạo phiên bản dùng chung của khóa 1 2 3 .. . # tạo khóa chung khóa = Khóa() Tiếp theo, chúng ta có thể tạo một luồng mới và định cấu hình nó để thực thi hàm task() của chúng ta và chuyển thể hiện của khóa dùng chung 1 2 3 .. . # tạo chủ đề mới luồng = Luồng(mục tiêu=task, args=(lock,)) Sau đó, luồng mới có thể được bắt đầu và luồng chính có thể chặn cho đến khi luồng mới kết thúc 1 2 3 4 5 .. . # bắt đầu chủ đề mới chuỗi. bắt đầu() # đợi chủ đề mới kết thúc chuỗi. tham gia() Liên kết điều này lại với nhau, ví dụ hoàn chỉnh được liệt kê bên dưới 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #Trăn Siêu Nhanh. com # ví dụ về khóa hàm từ bên trong hàm từ thời gian nhập ngủ từ luồng nhập Luồng từ luồng nhập Khóa
# chức năng được gọi trong một chủ đề mới def nhiệm vụ(khóa): # giành được khóa với khóa. # chặn trong giây lát ngủ(1)
# tạo khóa chung khóa = Khóa() # tạo chủ đề mới luồng = Luồng(mục tiêu=task, args=(lock,)) # bắt đầu chủ đề mới chuỗi. bắt đầu() # đợi chủ đề mới kết thúc chuỗi. tham gia() Chạy ví dụ đầu tiên sẽ tạo khóa dùng chung Tiếp theo, luồng mới được tạo và định cấu hình để chạy chức năng tác vụ của chúng tôi được gọi với khóa dùng chung làm đối số. Chủ đề mới sau đó được bắt đầu và khối chủ đề mới Chủ đề thực thi chức năng nhiệm vụ () trước tiên khóa chức năng bằng cách lấy khóa. Sau đó, nó thực thi phần thân của hàm, giải phóng khóa và quay trở lại từ hàm Tiếp theo, hãy xem một ví dụ về khóa hàm bằng cách sử dụng khóa làm biến toàn cục Chức năng khóa với biến toàn cầuChúng tôi có thể khóa một chức năng từ bên trong bằng cách sử dụng khóa toàn cầu Điều này có thể đạt được bằng cách xác định khóa là một biến toàn cục, sau đó khai báo phạm vi của khóa trong hàm trước khi sử dụng, đảm bảo sử dụng đúng phạm vi Sau khi khóa khả dụng, khóa có thể được lấy trong chức năng bằng giao diện trình quản lý ngữ cảnh, phần thân của chức năng thực thi, sau đó khóa có thể được giải phóng tự động trước khi chức năng quay trở lại Hàm task() bên dưới thực hiện điều này 1 2 3 4 5 6 7 8 # chức năng được gọi trong một chủ đề mới def nhiệm vụ(). # khai báo phạm vi của biến khóa toàn cục khóa toàn cầu # giành được khóa với khóa. # chặn trong giây lát ngủ(1) Khóa sau đó có thể được định nghĩa là một biến toàn cục 1 2 3 .. . # tạo khóa khóa = Khóa() Một luồng mới sau đó có thể được cấu hình để thực thi hàm task() của chúng ta 1 2 3 .. . # tạo chủ đề mới luồng = Luồng(mục tiêu=task) Cuối cùng, luồng mới có thể được bắt đầu và luồng chính có thể chặn cho đến khi luồng mới kết thúc 1 2 3 4 5 .. . # bắt đầu chủ đề mới chuỗi. bắt đầu() # đợi chủ đề mới kết thúc chuỗi. tham gia() Liên kết điều này lại với nhau, ví dụ hoàn chỉnh được liệt kê bên dưới 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #Trăn Siêu Nhanh. com # ví dụ về khóa hàm từ bên trong hàm từ thời gian nhập ngủ từ luồng nhập Luồng từ luồng nhập Khóa
# chức năng được gọi trong một chủ đề mới def nhiệm vụ(). # khai báo phạm vi của biến khóa toàn cục khóa toàn cầu # giành được khóa với khóa. # chặn trong giây lát ngủ(1)
# tạo khóa khóa = Khóa() # tạo chủ đề mới luồng = Luồng(mục tiêu=task) # bắt đầu chủ đề mới chuỗi. bắt đầu() # đợi chủ đề mới kết thúc chuỗi. tham gia() Chạy ví dụ đầu tiên sẽ tạo khóa dùng chung Tiếp theo, luồng mới được tạo và định cấu hình để chạy chức năng tác vụ của chúng tôi. Chủ đề mới sau đó được bắt đầu và khối chủ đề mới Chuỗi thực thi hàm task() trước tiên khóa chức năng bằng cách lấy khóa toàn cầu. Sau đó, nó thực thi phần thân của hàm, giải phóng khóa và quay trở lại từ hàm Khóa học lập trình Python miễn phí Tải xuống bảng cheat API luồng của tôi và như một phần thưởng, bạn sẽ có quyền truy cập MIỄN PHÍ vào khóa học email 7 ngày của tôi Khám phá cách sử dụng mô-đun phân luồng Python, bao gồm cách tạo và bắt đầu các luồng mới cũng như cách sử dụng khóa và ngữ nghĩa mutex |