2010-09-15 21 views
8

Tôi đang tìm một cách để gỡ lỗi một phần hiếm Delphi 7 phần quan trọng (TCriticalSection) treo/bế tắc. Trong trường hợp này, nếu một chuỗi đang chờ trên một phần quan trọng trong hơn 10 giây, tôi muốn tạo báo cáo với dấu vết ngăn xếp của cả hai luồng hiện đang khóa phần quan trọng và cũng là chuỗi không thể thực hiện được để khóa phần quan trọng sau khi chờ 10 giây. Đó là OK sau đó nếu một ngoại lệ được nâng lên hoặc các ứng dụng chấm dứt.Delphi: Gỡ lỗi phần quan trọng treo bằng cách báo cáo ngăn xếp cuộc gọi của chủ đề đang chạy trên khóa "thất bại"

Tôi muốn tiếp tục sử dụng các phần quan trọng, thay vì sử dụng các nguyên tắc đồng bộ hóa khác, nếu có thể, nhưng có thể chuyển đổi nếu cần thiết (chẳng hạn như để có tính năng hết thời gian chờ).

Nếu công cụ/phương pháp hoạt động ở thời gian chạy ngoài IDE, đây là phần thưởng vì điều này khó tái tạo theo yêu cầu. Trong trường hợp hiếm hoi, tôi có thể sao chép bế tắc bên trong IDE, nếu tôi cố gắng tạm dừng để bắt đầu gỡ lỗi, IDE chỉ ngồi đó không làm gì cả, và không bao giờ đến trạng thái mà tôi có thể xem chủ đề hoặc ngăn xếp cuộc gọi. Tôi có thể thiết lập lại chương trình đang chạy.

Cập nhật: Trong trường hợp này, tôi chỉ xử lý một phần quan trọng và 2 luồng, vì vậy đây có thể không phải là vấn đề đặt hàng khóa. Tôi tin rằng có một nỗ lực lồng nhau không đúng cách để nhập khóa qua hai luồng khác nhau, dẫn đến bế tắc.

Trả lời

8

Bạn nên tạo và sử dụng lớp đối tượng khóa của riêng mình. Nó có thể được thực hiện bằng cách sử dụng các phần quan trọng hoặc mutexes, tùy thuộc vào việc bạn muốn gỡ lỗi này hay không.

Tạo lớp học của riêng bạn có thêm lợi ích: Bạn có thể triển khai phân cấp khóa và tăng ngoại lệ khi bị vi phạm. Deadlocks xảy ra khi ổ khóa không được thực hiện theo đúng thứ tự, mỗi lần. Chỉ định một mức khóa cho mỗi khóa làm cho nó có thể kiểm tra xem các khóa được lấy theo đúng thứ tự hay không. Bạn có thể lưu trữ mức khóa hiện tại trong một luồng và chỉ cho phép các khóa được thực hiện có mức khóa thấp hơn, nếu không bạn sẽ tăng ngoại lệ. Điều này sẽ bắt tất cả các vi phạm, ngay cả khi không có bế tắc xảy ra, vì vậy nó sẽ đẩy nhanh quá trình gỡ lỗi của bạn rất nhiều.

Để nhận được dấu vết ngăn xếp của chủ đề, có nhiều câu hỏi ở đây trên Stack Overflow đối phó với điều này.

Cập nhật

Bạn viết:

Trong trường hợp này, tôi chỉ làm việc với một phần quan trọng và 2 chủ đề, vì vậy đây có thể không phải là một vấn đề khóa đặt hàng. Tôi tin rằng có một nỗ lực lồng nhau không đúng cách để nhập khóa qua hai luồng khác nhau, dẫn đến bế tắc.

Đó không thể là toàn bộ câu chuyện. Không có cách nào để bế tắc với hai chủ đề và một phần quan trọng một mình trên Windows, bởi vì các phần quan trọng có thể được mua ở đó một cách đệ quy bởi một luồng. Phải có một cơ chế chặn khác có liên quan, ví dụ như cuộc gọi SendMessage().

Nhưng nếu bạn thực sự chỉ xử lý hai luồng, thì một trong số chúng phải là luồng chính/VCL/GUI. Trong trường hợp đó, bạn sẽ có thể sử dụng tính năng MadExcept "Main thread freeze checking". Nó sẽ cố gắng gửi một tin nhắn đến chủ đề chính, và thất bại sau một thời gian tùy chỉnh đã trôi qua mà không có thông báo được xử lý. Nếu thread chính của bạn đang chặn trên phần quan trọng, và thread khác đang chặn trên một cuộc gọi xử lý tin nhắn thì MadExcept sẽ có thể nắm bắt được điều này và cung cấp cho bạn một dấu vết ngăn xếp cho cả hai luồng.

+1

+1 đối với kiểm tra cố định chủ đề của MadExcept. –

+0

madExcept cũng có thể được yêu cầu lấy một chuỗi kết xuất bất kỳ lúc nào, vì vậy có lẽ lý tưởng cho việc này. – mj2008

+0

madExcept trông giống như lựa chọn tốt nhất. Cảm ơn! – Anagoge

0

Nếu bạn muốn có thể chờ đợi điều gì đó với thời gian chờ, bạn có thể thử thay thế Phần quan trọng bằng tín hiệu TEvent. Bạn có thể nói để chờ đợi sự kiện, cho nó một thời gian chờ, và kiểm tra mã kết quả. Nếu tín hiệu được đặt, bạn có thể tiếp tục. Nếu không, nếu nó hết thời gian chờ, bạn sẽ đưa ra một ngoại lệ.

Ít nhất, đó là cách tôi sẽ làm điều đó trong D2010. Tôi không chắc chắn nếu Delphi 7 có TEvent, nhưng nó có thể làm.

3

Đây không phải là một câu trả lời trực tiếp cho câu hỏi của bạn, nhưng một cái gì đó tôi chạy vào gần đây đã có tôi (và một vài đồng nghiệp) stumped trong một thời gian.

Đó là một chuỗi liên tục treo, liên quan đến một phần quan trọng và một khi chúng ta biết nguyên nhân, nó rất rõ ràng và đã cho tất cả chúng ta một khoảnh khắc "d'oh". Tuy nhiên, nó đã có một số săn bắn nghiêm trọng để tìm (thêm ngày càng nhiều dấu vết đăng nhập để xác định các tuyên bố vi phạm) và đó là lý do tại sao tôi nghĩ rằng tôi muốn đề cập đến nó.

Nó cũng nằm trên phần quan trọng. Một chủ đề khác đã thực sự có được phần quan trọng đó. Một khóa chết như vậy dường như không phải là nguyên nhân, vì chỉ có một phần quan trọng liên quan, vì vậy không có vấn đề gì với việc lấy khóa theo thứ tự khác. Các chủ đề giữ phần quan trọng chỉ đơn giản là nên tiếp tục và sau đó phát hành khóa, cho phép các chủ đề khác để có được nó.

Cuối cùng, hóa ra chủ đề giữ khóa cuối cùng cũng truy cập vào ItemIndex của một combobox (IIRC), khá là vô hại. Thật không may, nhận được rằng ItemIndex phụ thuộc vào việc xử lý tin nhắn. Và các chủ đề chờ đợi cho khóa là chủ đề ứng dụng chính ... (chỉ trong trường hợp bất kỳ ai thắc mắc: các chủ đề chính hiện tất cả các tin nhắn xử lý ...)

Chúng ta có thể nghĩ về điều này sớm hơn rất nhiều nếu nó có rõ ràng hơn một chút ngay từ đầu rằng vcl đã được tham gia. Tuy nhiên, nó bắt đầu trong mã liên quan đến non-ui và sự tham gia vcl chỉ trở nên rõ ràng sau khi thêm công cụ (nhập - thoát truy tìm) dọc theo cây cuộc gọi và quay lại tất cả các sự kiện được kích hoạt và trình xử lý của chúng đến mã ui.

Chỉ hy vọng câu chuyện này sẽ giúp ích cho người nào đó phải đối mặt với hang bí ẩn.

2

Sử dụng Mutex thay vì Phần quan trọng. Có một sự khác biệt nhỏ giữa các mutex và các phần quan trọng - các phần quan trọng có hiệu quả hơn trong khi các mutex linh hoạt hơn. Bạn có thể dễ dàng chuyển đổi giữa các mutex và các phần quan trọng, bằng cách sử dụng ví dụ các mutex trong phiên bản gỡ lỗi.

cho phần quan trọng, chúng tôi sử dụng:

var 
    FLock: TRTLCriticalSection; 

    InitializeCriticalSection(FLock); // create lock 
    DeleteCriticalSection(FLock);  // free lock 
    EnterCriticalSection(FLock);  // acquire lock 
    LeaveCriticalSection(FLock);  // release lock 

cùng với mutex:

var FLock: THandle; 

    FLock:= CreateMutex(nil, False, nil); // create lock 
    CloseHandle(FLock);     // free lock 
    WaitForSingleObject(FLock, Timeout); // acquire lock 
    ReleaseMutex(FLock);     // release lock 

Bạn có thể sử dụng timeout (trong mili giây; 10000 trong 10 giây) với mutexes bằng cách thực hiện được chức năng khóa như này:

function AcquireLock(Lock: THandle; TimeOut: LongWord): Boolean; 
begin 
    Result:= WaitForSingleObject(Lock, Timeout) = WAIT_OBJECT_0; 
end; 
1

Bạn cũng có thể sử dụng Mục quan trọng với TryEnterCriticalSection API thay vì EnterCriticalSection.

Nếu bạn sử dụng TryEnterCriticalSection và chuyển đổi khóa không thành công, API trả về False và bạn có thể xử lý lỗi theo bất kỳ cách nào bạn thấy phù hợp, thay vì chỉ khóa luồng.

Something như

while not TryEnterCriticalSection(fLock) and (additional_checks) do 
begin 
    deal_with_failure(); 
    sleep(500); // wait 500 ms 
end; 

Do lưu ý rằng Delphi TCriticalSection sử dụng EnterCriticalSection vì vậy trừ khi bạn tinh chỉnh lớp đó, bạn sẽ phải làm lớp của riêng bạn hoặc bạn sẽ phải đối phó với các mục Critical khởi/deinitialization.

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