2011-06-18 31 views
5

Tôi đã đọc rất nhiều ví dụ về các chủ đề khóa .. nhưng tại sao bạn nên khóa chúng? Từ sự hiểu biết của tôi, khi bạn khởi tạo chủ đề mà không tham gia cùng họ, họ sẽ cạnh tranh với chủ đề chính và tất cả các luồng khác cho tài nguyên và sau đó thực hiện, đôi khi đồng thời, đôi khi không.Tại sao bạn nên khóa luồng?

Khóa có đảm bảo rằng các chuỗi KHÔNG thực thi đồng thời không?

Ngoài ra, có vấn đề gì với các chuỗi thực thi đồng thời? Thậm chí còn tốt hơn sao? (thực thi tổng thể nhanh hơn)

Khi bạn khóa chủ đề, nó sẽ khóa tất cả hay bạn có thể chọn chủ đề nào bạn muốn khóa không? (Dù khóa thực sự làm ...)

tôi đề cập đến việc sử dụng chức năng khóa như khóa() và có được trong mô-đun threading btw ...

+0

Không, tôi không hỏi về GIL, tôi biết những hạn chế của nó trong python và tôi hài lòng với nó, câu hỏi là về khóa luồng với get() và release() không liên quan đến GIL (ngoài nó đã khóa tên) – MistahX

+1

Đã gắn thẻ lại, không độc quyền với 'python'. –

+0

được gắn thẻ lại là python, tôi đang đề cập đến các phương pháp khóa trong mô-đun luồng, quên thêm vào trong – MistahX

Trả lời

11

Một khóa cho phép bạn buộc nhiều chủ đề để truy cập một tài nguyên tại một thời điểm, thay vì tất cả chúng cố truy cập tài nguyên cùng một lúc.

Như bạn lưu ý, thông thường bạn sẽ muốn các chuỗi thực thi đồng thời. Tuy nhiên, hãy tưởng tượng rằng bạn có hai luồng và cả hai đều ghi vào cùng một tệp. Nếu họ cố gắng viết vào cùng một tập tin cùng một lúc, đầu ra của họ sẽ bị xen kẽ và không phải luồng nào sẽ thực sự thành công khi đưa vào tệp những gì họ muốn.

Có thể sự cố này sẽ không xảy ra. Hầu hết thời gian, các chủ đề sẽ không cố gắng ghi vào tập tin cùng một lúc. Nhưng đôi khi, có thể một lần trong một nghìn lần chạy, họ làm. Vì vậy, có thể bạn có một lỗi xảy ra dường như ngẫu nhiên và khó tái tạo và do đó khó khắc phục. Ugh! Hoặc có lẽ ... và điều này đã xảy ra tại công ty tôi làm việc cho ... bạn có lỗi như vậy nhưng không biết họ đang có bởi vì hầu như không có bất kỳ khách hàng của bạn có hơn 4 CPU. Sau đó, tất cả bắt đầu mua các hộp 16-CPU ... và phần mềm của bạn chạy như nhiều luồng như có lõi CPU, vì vậy bây giờ có 4 lần nhiều chủ đề và đột nhiên bạn đang gặp rất nhiều hoặc nhận được kết quả sai.

Vì vậy, hãy quay lại tệp. Để ngăn chặn các chủ đề từ bước vào nhau, mỗi thread phải có được một khóa trên tập tin trước khi ghi vào nó. Chỉ một luồng có thể giữ khóa tại một thời điểm, do đó, chỉ có một luồng có thể ghi vào tệp tại một thời điểm. Chủ đề giữ khóa cho đến khi nó được thực hiện bằng văn bản cho tập tin, sau đó phát hành khóa để thread khác có thể sử dụng tập tin.

Nếu chủ đề đang ghi vào các tệp khác nhau, vấn đề này không bao giờ phát sinh. Vì vậy, đó là một trong những giải pháp: có chủ đề của bạn ghi vào các tập tin khác nhau, và kết hợp chúng sau đó nếu cần thiết. Nhưng điều này không phải lúc nào cũng có thể; đôi khi, chỉ có một thứ gì đó.

Nó không phải là tệp. Giả sử bạn đang cố gắng đếm số lần xuất hiện của chữ "A" trong một loạt các tệp khác nhau, một luồng cho mỗi tệp. Bạn nghĩ rằng, tốt, rõ ràng, tôi sẽ chỉ có tất cả các chủ đề tăng cùng một vị trí bộ nhớ mỗi khi họ nhìn thấy một "A." Nhưng! Khi bạn tăng số biến giữ số đếm, máy tính đọc biến đó thành sổ đăng ký, tăng số lượng đăng ký và sau đó lưu lại giá trị đó. Điều gì sẽ xảy ra nếu hai luồng đọc giá trị cùng một lúc, tăng cùng một lúc và lưu trữ lại cùng một lúc? Cả hai đều bắt đầu lúc, nói, 10, tăng nó lên 11, lưu trữ 11 trở lại. Vì vậy, 11 của truy cập khi nó nên là 12: bạn đã mất một số.

Ổ khóa có thể tốn kém, vì bạn phải đợi cho đến khi bất kỳ ai khác đang sử dụng tài nguyên được thực hiện với nó. Đây là lý do tại sao Khóa thông dịch toàn cầu của Python là một nút cổ chai hiệu suất.Vì vậy, bạn có thể quyết định tránh sử dụng tài nguyên được chia sẻ. Thay vì sử dụng một vị trí bộ nhớ duy nhất để giữ số lượng "A" trong các tệp của bạn, mỗi chuỗi giữ số đếm của riêng nó và bạn thêm tất cả vào cuối (tương tự như giải pháp tôi đã đề xuất với các tệp, vui vẻ đủ) .

+0

Được rồi điều này có ý nghĩa, nhưng các ổ khóa tôi đang nói về chủ đề khóa, không phải tập tin? Vì vậy, họ phải làm những gì? – MistahX

+1

@MistahX: Không, họ khóa bất cứ điều gì _you_ quyết định họ khóa. Bạn sử dụng chúng như bạn thấy phù hợp để ngăn chặn nhiều chủ đề làm cùng một điều cùng một lúc. Chúng là nguyên thủy, bạn xây dựng từ chúng những gì bạn cần. Nếu bạn truy cập vào một tập tin trong một khóa, bạn có hiệu quả "khóa" tập tin đó. –

+1

Đó là khóa tài nguyên, không phải là chuỗi thực tế. – Alan

9

Đầu tiên, khóa được thiết kế để bảo vệ tài nguyên; chủ đề không được 'khóa' hoặc 'mở khóa' chúng/có được/khóa (trên tài nguyên) và/release/a lock (trên tài nguyên).

Bạn là chính xác mà bạn muốn đề để chạy đồng thời càng nhiều càng tốt, nhưng chúng ta hãy nhìn vào điều này:

y=10 

def doStuff(x): 
    global y 
    a = 2 * y 
    b = y/5 
    y = a + b + x 
    print y 

t1 = threading.Thread(target=doStuff, args=(8,)) 
t2 = threading.Thread(target=doStuff, args=(8,)) 
t1.start() 
t2.start() 
t1.join() 
t2.join() 

Bây giờ, bạn có thể biết rằng hoặc là một trong những chủ đề có thể hoàn thành và in đầu tiên . Bạn sẽ thấy cả hai đầu ra 30.

Nhưng chúng có thể không.

y là tài nguyên được chia sẻ và trong trường hợp này, các bit đọc và ghi vào y là một phần của cái được gọi là "phần quan trọng" và phải được bảo vệ bằng khóa. Lý do là bạn không nhận được đơn vị công việc: một trong hai luồng có thể đạt được CPU bất cứ lúc nào.

Hãy suy nghĩ về nó như thế này:

t1 được hạnh phúc thực thi mã và nó chạm

a = 2 * y 

Bây giờ t1 có a = 20 và dừng thực hiện trong một thời gian. t2 trở nên hoạt động trong khi t1 chờ thêm thời gian CPU. t2 thực hiện:

a = 2 * y 
b = y/5 
y = a + b + x 

vào thời điểm này các biến toàn cục y = 30

t2 dừng dừng một chút và t1 nhặt lên một lần nữa. nó thực thi:

b = y/5 
y = a + b + x 

Kể từ khi y là 30 khi b đã được thiết lập, b = 6 và y hiện đang thiết lập để 34.

thứ tự của các bản in là không xác định làm tốt và bạn có thể nhận được các 30 đầu tiên hoặc 34 đầu tiên.

sử dụng một khóa, chúng tôi sẽ có:

global l 
l = threading.Lock() 
def doStuff(x): 
    global y 
    global l 
    l.acquire() 
    a = 2 * y 
    b = y/5 
    y = a + b + x 
    print y 
    l.release() 

này nhất thiết phải làm cho phần này của mã tuyến tính - chỉ có một thread tại một thời điểm. Nhưng nếu toàn bộ chương trình của bạn là tuần tự bạn không nên sử dụng chủ đề anyway. Ý tưởng là bạn tăng tốc độ dựa trên tỷ lệ phần trăm mã bạn có thể thực thi các khóa bên ngoài và chạy song song. Đây là (một lý do) tại sao sử dụng các luồng trên hệ thống 2 lõi không tăng gấp đôi hiệu suất cho mọi thứ.

bản thân khóa cũng là tài nguyên được chia sẻ, nhưng nó cần phải là: một khi một chuỗi mua khóa, tất cả các chủ đề khác cố gắng lấy/cùng/khóa sẽ chặn cho đến khi nó được giải phóng. Khi nó được phát hành, luồng đầu tiên để di chuyển về phía trước và lấy khóa sẽ chặn tất cả các luồng chờ khác.

Hy vọng điều đó là đủ để tiếp tục!

Các vấn đề liên quan