2013-08-23 50 views
8

giải pháp chính xác để đảm bảo rằng tệp sẽ không bao giờ bị hỏng khi sử dụng nhiều luồng và quy trình.đồng thời ghi vào cùng một tệp bằng cách sử dụng các chủ đề và quy trình

phiên bản cho chủ đề, quan tâm đến việc mở lỗi.

lock = threading.RLock() 
with lock: 
    try: 
    f = open(file, 'a') 
    try: 
     f.write('sth') 
    finally: 
     f.close() # try close in any circumstances if open passed 
    except: 
    pass # when open failed 

cho quá trình tôi đoán phải sử dụng multiprocessing.Lock

nhưng nếu tôi muốn 2 quy trình và quá trình đầu tiên của 2 bài (mỗi một file sử dụng)

đó chỉ là lý thuyết, nhưng Tôi muốn biết làm thế nào để trộn đồng bộ với chủ đề và quy trình. là chủ đề "kế thừa" nó từ quá trình?, Vì vậy chỉ đồng bộ hóa giữa các quy trình được yêu cầu?

và 2. Tôi không chắc chắn nếu mã trên cần lồng thử trong trường hợp khi ghi sẽ thất bại, và chúng tôi muốn đóng mở file (nếu nó sẽ vẫn mở sau khi khóa phát hành)

+0

Như một lưu ý phụ, 'try' /' final' của bạn có thể (và thường nên) được thay thế bằng câu lệnh 'with'. Ngoài ra, 'ngoại trừ: pass' thường là một ý tưởng tồi - nếu bạn đang cố gắng nuốt một ngoại lệ cụ thể, chỉ cần nuốt một điều đặc biệt, không phải tất cả mọi thứ. Nếu bạn nhìn quanh đây, bạn sẽ tìm thấy hàng chục câu hỏi từ những người có lỗi ngớ ngẩn như chuyển một int như một tên tập tin mà một trần ngoại trừ ngăn cản họ nhận thấy và gỡ lỗi. – abarnert

+0

Ngoài ra, nếu bạn đang khóa cụ thể cho các tệp, bạn có thể muốn xem xét sử dụng khóa tệp tư vấn trên POSIX và quyền truy cập tệp độc quyền trên Windows, thay vì khóa chung/xử lý chung. – abarnert

+1

Một khả năng khác là làm tất cả các tập tin phụ thêm từ một luồng đơn (trong một tiến trình), và để mọi người khác chỉ đăng tin nhắn lên hàng đợi (không cần đồng bộ hóa, vì nó được tích hợp sẵn). – abarnert

Trả lời

7

Trong khi isn này' t hoàn toàn rõ ràng từ the docs, đa xử lý đồng bộ nguyên thủy làm trong thực tế đồng bộ hóa chủ đề là tốt.

Ví dụ, nếu bạn chạy mã này:

import multiprocessing 
import sys 
import threading 
import time 

lock = multiprocessing.Lock() 

def f(i): 
    with lock: 
     for _ in range(10): 
      sys.stderr.write(i) 
      time.sleep(1) 

t1 = threading.Thread(target=f, args=['1']) 
t2 = threading.Thread(target=f, args=['2']) 
t1.start() 
t2.start() 
t1.join() 
t2.join() 

... đầu ra sẽ luôn luôn được 1111111111222222222 hoặc 22222222221111111111, không phải là một hỗn hợp của cả hai.

Ổ khóa được triển khai trên các đối tượng đồng bộ hạt nhân Win32 trên Windows, các ẩn dụ trên nền tảng POSIX hỗ trợ chúng và không được triển khai ở tất cả các nền tảng khác. (Bạn có thể kiểm tra điều này với import multiprocessing.semaphore, mà sẽ nâng cao một ImportError trên các nền tảng khác, như được giải thích trong tài liệu.)


Điều đó đang được nói, nó chắc chắn an toàn có hai cấp độ của ổ khóa, miễn là bạn luôn sử dụng chúng theo đúng thứ tự - nghĩa là không bao giờ lấy threading.Lock trừ khi bạn có thể đảm bảo rằng quy trình của bạn có số multiprocessing.Lock.

Nếu bạn làm điều này đủ khéo léo, nó có thể có lợi ích hiệu suất. (Khóa chéo xử lý trên Windows và trên một số nền tảng POSIX, có thể là đơn đặt hàng có cường độ chậm hơn so với ổ khóa trong quá trình.)

Nếu bạn chỉ làm điều đó một cách rõ ràng (chỉ làm with threadlock: bên trong with processlock: khối), rõ ràng sẽ không giúp hiệu suất, và trong thực tế sẽ làm chậm những thứ xuống một chút (mặc dù khá có thể không đủ để đo lường), và nó sẽ không thêm bất kỳ lợi ích trực tiếp. Tất nhiên độc giả của bạn sẽ biết rằng mã của bạn là chính xác ngay cả khi họ không biết rằng khóa multiprocessing hoạt động giữa các chủ đề và trong một số trường hợp, việc gỡ rối các quá trình xử lý nội bộ có thể dễ dàng hơn nhiều so với gỡ lỗi các quá trình liên kết ... nhưng tôi không nghĩ đó là một lý do đủ tốt cho sự phức tạp thêm trong hầu hết các trường hợp.