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_compilescript = "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
- Khóa được mua và giải phóng bởi người gọi chức năng
- 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ề
- lấy khóa
- chức năng gọi
- 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
- 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
- Đả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
- Xác định khóa là một biến toàn cục
- 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