2010-10-02 21 views
29

Tôi có một chức năng pthread Á hậu joinable được xác định như sau:pthread_exit vs trở

void *sumOfProducts(void *param) 
{ 
... 
pthread_exit(0); 
} 

chủ đề này được cho là tham gia các chủ đề chính.

Bất cứ khi nào tôi chạy chương trình của tôi qua Valgrind tôi sẽ nhận được rò rỉ sau:

LEAK SUMMARY: 
    definitely lost: 0 bytes in 0 blocks 
    indirectly lost: 0 bytes in 0 blocks 
    possibly lost: 0 bytes in 0 blocks 
    still reachable: 968 bytes in 5 blocks 
     suppressed: 0 bytes in 0 blocks 

ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 15 from 10) 

Tôi đã kiểm tra man page của pthreads mà nói:

The new thread terminates in one of the following ways: 

    * It calls pthread_exit(3), specifying an exit status value that is 
    available to another thread in the same process that calls 
    pthread_join(3). 

    * It returns from start_routine(). This is equivalent to calling 
    pthread_exit(3) with the value supplied in the return statement. 

    * It is canceled (see pthread_cancel(3)). 

    * Any of the threads in the process calls exit(3), or the main thread 
    performs a return from main(). This causes the termination of all 
    threads in the process. 

Kỳ diệu thay, khi tôi thay thế pthread_exit() với câu lệnh return, rò rỉ biến mất.

return(NULL); 

câu hỏi thực tế của tôi là ba mũi nhọn:

  1. Ai đó có thể giải thích tại sao câu lệnh return đã không có rò rỉ?
  2. Có một số khác biệt cơ bản giữa cả hai câu lệnh, liên quan đến thoát khỏi chuỗi không?
  3. Nếu có, khi nào người ta nên ưa thích người kia?
+0

Bạn có thực sự sử dụng C++ không? C++ sử dụng phạm vi để phá hủy các đối tượng và trả về sẽ "để lại" phạm vi đó trong khi pthread_exit sẽ không. –

+0

Tôi xin lỗi nhưng tôi không bao giờ đề cập đến C++ ở bất cứ đâu trong câu hỏi của tôi. Tôi đang làm tất cả mọi thứ trong C như của bây giờ. –

+1

Tôi biết bạn đã không đề cập đến nó, nhưng đó là một đoán, đó là lý do tại sao tôi hỏi. :) Bạn có thể cung cấp [test] hoàn chỉnh (http://sscce.org/) [case] (http://www.xs4all.nl/~weegen/eelis/iso-c++/testcase.xhtml) không? –

Trả lời

34

sau Trường hợp thử nghiệm tối thiểu thể hiện hành vi mà bạn mô tả:

#include <pthread.h> 
#include <unistd.h> 

void *app1(void *x) 
{ 
    sleep(1); 
    pthread_exit(0); 
} 

int main() 
{ 
    pthread_t t1; 

    pthread_create(&t1, NULL, app1, NULL); 
    pthread_join(t1, NULL); 

    return 0; 
} 

valgrind --leak-check=full --show-reachable=yes thấy 5 khối phân bổ từ chức năng gọi bằng pthread_exit() đó là unfreed nhưng vẫn có thể truy cập tại lối ra quá trình. Nếu pthread_exit(0); được thay thế bằng return 0;, 5 khối không được cấp phát.

Tuy nhiên, nếu bạn thử tạo và tham gia nhiều chuỗi chủ đề, bạn sẽ thấy rằng số lượng bộ nhớ không xác định được sử dụng tại lối ra không không tăng. Điều này, và thực tế là nó vẫn có thể truy cập, chỉ ra rằng bạn chỉ thấy một sự kỳ quặc của việc thực hiện glibc. Một số hàm glibc phân bổ bộ nhớ với malloc() lần đầu tiên chúng được gọi, mà chúng tiếp tục được cấp phát cho phần còn lại của thời gian xử lý. glibc không bận tâm để giải phóng bộ nhớ này khi thoát khỏi quá trình, vì nó biết rằng quá trình này đang bị phá hủy dù sao - nó chỉ là một sự lãng phí các chu kỳ CPU.

+0

Không có cách nào để buộc bộ nhớ free'ing phân bổ bởi glibc? Không phải là nó nessescary, nhưng tôi nghĩ rằng tôi đã nhìn thấy nó đề cập đến một nơi nào đó trên trang web này ... – Christoffer

+2

@Christoffer: Valgrind, kể từ khoảng phiên bản 1.1 hay như vậy, gọi một hàm có tên '__libc_freeres' được cho là để làm điều này. Tuy nhiên, có một số phiên bản glibc có lỗi trong hàm này (vì nó thường không được gọi trừ khi chương trình đang chạy dưới trình gỡ lỗi bộ nhớ). Nó thường được gọi theo mặc định, trừ khi valgrind được chạy với đối số '--run-libc-freeres = no'. – Doug

+0

Cảm ơn bạn! Điều này khiến tôi phát điên. Tôi nghĩ đó là một sự kỳ lạ glibc, nhưng tôi không chắc chắn. – William

0

Bạn có thực sự đang sử dụng C++ không? Để làm rõ - tệp nguồn của bạn kết thúc bằng một tiện ích mở rộng .c và bạn đang biên dịch nó với gcc, không phải g++?

Có vẻ như khả năng hợp lý của bạn là phân bổ tài nguyên mà bạn mong muốn được dọn sạch tự động khi hàm trả về. Các đối tượng C++ cục bộ như std::vector hoặc std::string làm điều này và các trình phá hủy của chúng có thể sẽ không chạy nếu bạn gọi pthread_exit nhưng sẽ bị xóa nếu bạn chỉ cần quay lại.

Tùy chọn của tôi là để tránh các API cấp thấp như pthread_exit và luôn trả về từ hàm chuỗi, nếu có thể. Chúng tương đương, ngoại trừ pthread_exit là cấu trúc kiểm soát lưu lượng thực tế bỏ qua ngôn ngữ bạn đang sử dụng, nhưng return thì không.

+0

Tôi đang sử dụng chỉ C. –

+0

Và bạn đang biên dịch với 'gcc', và không có khả năng là mã hoặc bất kỳ mã nào mà nó gọi có thể vô tình sử dụng tính năng C++? – Doug

+0

Hãy yên tâm, tôi đang biên soạn với gcc. Hãy nhìn vào các thẻ, trước khi trả lời. –

0

Tôi có kinh nghiệm rằng valgrind gặp khó khăn trong việc theo dõi dung lượng được phân bổ cho trạng thái của các chuỗi có thể nối. (Điều này đi theo cùng hướng như caf chỉ ra.)

Vì có vẻ như bạn luôn trả về giá trị 0 Tôi đoán rằng bạn có thể cần phải tham gia chủ đề của mình từ một quan điểm ứng dụng? Nếu xem xét việc khởi chạy chúng tách ra từ đầu, điều này tránh việc phân bổ bộ nhớ đó.

Nhược điểm là bạn có thể sở:

  1. để thực hiện rào cản của riêng bạn tại cuối main của bạn. Nếu bạn biết số lượng các chủ đề trước đây là , một số được phân bổ tĩnh đơn giản pthread_barrier sẽ thực hiện.
  2. hoặc để thoát khỏi bạn main với pthread_exit sao cho bạn không giết phần còn lại của chuỗi đang chạy mà có thể chưa được hoàn tất.
10

Không chắc chắn nếu bạn vẫn quan tâm đến điều này, nhưng tôi hiện đang gỡ lỗi một tình huống tương tự. Các chủ đề sử dụng pthread_exit khiến cho valgrind báo cáo các khối có thể truy cập. Lý do dường như được giải thích khá tốt ở đây:

https://bugzilla.redhat.com/show_bug.cgi?id=483821

Về cơ bản có vẻ như pthread_exit gây ra một dlopen mà không bao giờ được làm sạch một cách rõ ràng khi các lối ra quá trình.

0

Dường như gọi exit() (và, rõ ràng, pthread_exit()) để các biến được phân bổ tự động được phân bổ. Bạn phải quay trở lại hoặc ném để thư giãn đúng cách.

mỗi C++ valgrind possible leaks on STL string:

@Klaim: Tôi không thấy nơi tài liệu mà nói rằng tôi sai, nhưng nếu nó thì nó là sai. Để trích dẫn tiêu chuẩn C++ (§18.3/8): "Các đối tượng tự động không bị hủy do gọi exit()." - James McNellis 10 Tháng 9 '10 19:11

Kể từ khi làm một "return 0" thay vì "pthread_exit (0)" dường như để giải quyết vấn đề của bạn (và của tôi .. cảm ơn), tôi giả rằng hành vi là tương tự giữa hai.

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