2009-06-11 30 views
27

Một trong những điều khó khăn nhất để tôi có thể điều chỉnh ban đầu là lập trình kinh nghiệm đầu tiên với pthreads trong C. Tôi đã từng biết chính xác dòng mã tiếp theo sẽ chạy và các kỹ thuật gỡ lỗi của tôi tập trung xung quanh kỳ vọng đó.Lập trình C: Gỡ lỗi với pthreads

Một số kỹ thuật tốt để gỡ lỗi với pthreads trong C là gì? Bạn có thể đề xuất các phương pháp cá nhân mà không cần thêm bất kỳ công cụ, công cụ nào bạn sử dụng hoặc bất kỳ thứ gì khác giúp bạn gỡ lỗi.

P.S. Tôi làm lập trình C của tôi bằng cách sử dụng gcc trong linux, nhưng đừng để điều đó nhất thiết kiềm chế câu trả lời của bạn

+3

+1 vì tôi nghĩ đây là một câu hỏi tuyệt vời với rất nhiều 'AHA!' - tiềm năng – tr9sh

Trả lời

29

Valgrind là một công cụ tuyệt vời để tìm các điều kiện chủng tộc và lạm dụng API pthreads. Nó giữ một mô hình của bộ nhớ chương trình (và có lẽ các tài nguyên chia sẻ) truy cập và sẽ phát hiện ổ khóa bị mất ngay cả khi lỗi là lành tính (điều này có nghĩa là nó sẽ hoàn toàn trở nên kém lành tính ở một số điểm sau này).

Để sử dụng nó, bạn gọi valgrind --tool=helgrind, here is its manual. Ngoài ra, có valgrind --tool=drd (manual). Helgrind và DRD sử dụng các mô hình khác nhau để chúng phát hiện chồng chéo nhưng có thể có các bộ lỗi khác nhau. Sai tích cực cũng có thể xảy ra.

Dù sao, valgrind đã tiết kiệm vô số giờ gỡ lỗi (không phải tất cả trong số họ mặc dù :) đối với tôi.

+0

+1 cho Valgrind –

+1

Có và sử dụng tệp --suppressions để bỏ qua các kết quả dương tính giả. Điều này đặc biệt cần thiết nếu bạn đang sử dụng C++ STL. – rleir

+1

Dude bài đăng này đã lưu mông của tôi !!!!! Tôi đã dành nhiều ngày mà không thể gỡ lỗi chương trình và sau khi tôi tìm thấy bài đăng này, tôi đã khắc phục sự cố trong giờ! CẢM ƠN BẠN! – user381261

2

Gỡ lỗi một ứng dụng đa luồng là khó khăn. Một trình gỡ rối tốt như GDB (với tùy chọn DDD giao diện người dùng) cho môi trường * nix hoặc một trong đó đi kèm với Visual Studio trên cửa sổ sẽ giúp rất nhiều.

+2

DDD chỉ là giao diện đồ họa cho gdb (và các trình gỡ rối khác); nó không thực sự là một trình gỡ lỗi chính nó –

+1

Đúng vậy. Đã sửa. – luke

0

Tôi có xu hướng sử dụng nhiều điểm ngắt. Nếu bạn không thực sự quan tâm đến chức năng thread, nhưng hãy quan tâm đến các tác dụng phụ của nó, thời điểm tốt để kiểm tra chúng có thể đúng trước khi nó thoát ra hoặc lặp lại trạng thái chờ hoặc bất kỳ thứ gì khác đang thực hiện.

7

Một trong những điều sẽ làm bạn ngạc nhiên khi gỡ lỗi các chương trình luồng là bạn sẽ thường xuyên tìm thấy các thay đổi của lỗi hoặc thậm chí biến mất khi bạn thêm printf hoặc chạy chương trình trong trình gỡ rối (được gọi là Heisenbug).

Trong chương trình có chuỗi, Heisenbug thường có nghĩa là bạn có race condition. Một lập trình viên giỏi sẽ tìm kiếm các biến được chia sẻ hoặc các tài nguyên phụ thuộc vào thứ tự. Một lập trình viên crappy sẽ cố gắng sửa chữa một cách mù quáng bằng các câu lệnh sleep().

1

cách tiếp cận của tôi để đa luồng gỡ lỗi tương tự như đơn luồng, nhưng nhiều thời gian hơn thường được chi tiêu trong giai đoạn suy nghĩ:

  1. Phát triển một lý thuyết về những gì có thể gây ra vấn đề.

  2. Xác định loại kết quả nào có thể được mong đợi nếu lý thuyết là đúng.

  3. Nếu cần, hãy thêm mã có thể bác bỏ hoặc xác minh kết quả và lý thuyết của bạn.

  4. Nếu lý thuyết của bạn là đúng, hãy khắc phục sự cố.

Thông thường, 'thử nghiệm' chứng minh lý thuyết là thêm phần quan trọng hoặc đột biến xung quanh mã nghi ngờ.Sau đó tôi sẽ cố gắng thu hẹp vấn đề bằng cách thu hẹp phần quan trọng một cách có hệ thống. Các phần quan trọng không phải lúc nào cũng là bản sửa lỗi tốt nhất (mặc dù thường có thể là bản sửa lỗi nhanh). Tuy nhiên, chúng hữu ích cho việc xác định 'khẩu súng hút'.

Giống như tôi đã nói, các bước tương tự áp dụng cho gỡ lỗi một luồng, mặc dù nó quá dễ dàng để chỉ cần nhảy vào trình gỡ rối và có nó. Gỡ lỗi nhiều luồng yêu cầu sự hiểu biết mạnh mẽ hơn về mã, vì tôi thường thấy mã đa luồng đang chạy thông qua trình gỡ lỗi không mang lại bất kỳ điều gì hữu ích.

Ngoài ra, hellgrind là một công cụ tuyệt vời. Trình kiểm tra chuỗi của Intel thực hiện một chức năng tương tự cho Windows, nhưng chi phí nhiều hơn so với địa ngục.

2

Trong giai đoạn 'suy nghĩ', trước khi bạn bắt đầu viết mã, hãy sử dụng khái niệm Máy trạng thái. Nó có thể làm cho thiết kế rõ ràng hơn nhiều.

printf's có thể giúp bạn hiểu được động lực của chương trình của bạn. Nhưng chúng làm lộn xộn mã nguồn, vì vậy hãy sử dụng một macro DEBUG_OUT() và trong định nghĩa của nó cho phép nó với một cờ boolean. Vẫn tốt hơn, hãy đặt/xóa cờ này bằng tín hiệu mà bạn gửi qua 'kill -USR1'. Gửi đầu ra tới tệp nhật ký có dấu thời gian.

cũng xem xét sử dụng assert(), sau đó phân tích các vùng lõi của bạn bằng gdb và ddd.

0

Khi tôi bắt đầu thực hiện lập trình đa luồng, tôi ... ngừng sử dụng trình gỡ rối. Đối với tôi, điểm mấu chốt là phân tách và đóng gói chương trình tốt.

Màn hình là cách dễ nhất để lập trình đa luồng không có lỗi. Nếu bạn không thể tránh các phụ thuộc khóa phức tạp thì bạn có thể dễ dàng kiểm tra xem chúng có là chu kỳ - đợi cho đến khi chương trình treo ans kiểm tra stacktraces bằng 'pstack'. Bạn có thể ngắt khóa tuần hoàn bằng cách giới thiệu một số chuỗi mới và bộ đệm truyền thông không đồng bộ.

Sử dụng xác nhận và đảm bảo ghi unittests đơn lẻ cho các thành phần cụ thể trong phần mềm của bạn - sau đó bạn có thể chạy chúng trong trình gỡ lỗi nếu muốn.

1

Tôi phát triển khá nhiều trong một thế giới hiệu suất cao đa luồng độc quyền, vì vậy đây là thực tế chung tôi sử dụng.

Thiết tạo mẫu cho việc tối ưu hóa tốt nhất là một thuật toán tốt hơn:

1) Phá vỡ bạn chức năng thành từng miếng một cách logic tách. Điều này có nghĩa rằng một cuộc gọi không "A" và CHỈ "A" - không A sau đó B sau đó C ...
2) KHÔNG CÓ HIỆU ỨNG BÊN: Xóa tất cả các biến toàn cầu trần trụi, tĩnh hay không. Nếu bạn không thể loại bỏ hoàn toàn tác dụng phụ, hãy cô lập chúng ở một vài địa điểm (tập trung chúng vào mã).
3) Làm cho nhiều thành phần bị cô lập RE-ENTRANT càng tốt. Điều này có nghĩa là chúng không có trạng thái phi trạng thái - chúng lấy tất cả các đầu vào của chúng như các hằng số và chỉ thao tác các tham số hằng số hợp lý, DECLARED để tạo ra đầu ra. Vượt qua giá trị thay vì tham chiếu bất cứ nơi nào bạn có thể.
4) Nếu bạn có trạng thái, hãy tách biệt rõ ràng giữa các hội đồng không quốc tịch và máy trạng thái thực tế. Lý tưởng nhất là máy trạng thái sẽ là một hàm duy nhất hoặc lớp điều khiển các thành phần không trạng thái.

Debugging:

lỗi Threading có xu hướng đi trong 2 chủng tộc flavors- rộng và sự bế tắc. Theo quy định, deadlocks có nhiều xác định hơn.

1) Bạn có thấy dữ liệu bị hỏng không?: YES => Có lẽ là một cuộc đua.
2) Có phát sinh lỗi trên MỌI chạy hay chỉ một số lần chạy ?: YES => Có thể là bế tắc (các cuộc đua thường không xác định).
3) Quy trình có treo không ?: YES => Có bế tắc ở đâu đó. Nếu nó chỉ treo đôi khi, bạn có thể có một cuộc đua quá.

Các điểm ngắt thường hoạt động giống như đồng bộ hóa nguyên thủy THEMSELVES trong mã, vì chúng tương tự về mặt logic - chúng buộc thực thi trong ngữ cảnh hiện tại cho đến khi một số ngữ cảnh khác (bạn) gửi tín hiệu tiếp tục. Điều này có nghĩa rằng bạn nên xem bất kỳ điểm ngắt bạn có trong mã như thay đổi hành vi mufti-ren của nó, và breakpoints SILL ảnh hưởng đến điều kiện chủng tộc nhưng (nói chung) không deadlocks.

Theo quy tắc, điều này có nghĩa là bạn nên xóa tất cả các điểm ngắt, xác định loại lỗi, THEN giới thiệu lại chúng để thử và sửa lỗi. Nếu không, họ chỉ đơn giản là bóp méo mọi thứ hơn nữa.

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