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

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ục

Dưới đây là mã biên dịch tệp PY thành tệp PYC

Note:
PYC files run even if the PY files are not present.
Code:
import py_compile

script = "C:\\temp\\myscript.py"
py_compile.compile[script]

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.
- https. // tài liệu. con trăn. org/3/library/luồng. html.

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

  • Luồng trong Python. Hướng dẫn hoàn chỉnh

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ăng

Mộ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 Mutex

Khó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

  • Cách sử dụng Khóa Mutex trong Python

Khóa chức năng

Có hai cách để khóa một chức năng

họ đang

  1. Khóa được mua và giải phóng bởi người gọi chức năng
  2. Khóa được mua và giải phóng trong chức năng

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ọi

Mộ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ề

  1. lấy khóa
  2. chức năng gọi
  3. Phát hành khóa

Đ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

  1. Kiểm tra cẩn thận mã của bạn và tìm tất cả các cuộc gọi đến chức năng và bảo vệ chúng bằng khóa
  2. Đảm bảo tất cả các cuộc gọi đến chức năng trong tương lai được bảo vệ bằng khóa

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

  1. Xác định khóa là một biến toàn cục
  2. Truyền khóa cho chức năng mỗi khi nó được gọi

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?
Tải xuống bảng cheat PDF MIỄN PHÍ của tôi

Ví dụ về Khóa chức năng

Chú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à

  • Truyền khóa làm đối số cho hàm
  • Xác định khóa dưới dạng biến toàn cục

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ầu

Chú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

Chủ Đề