2008-11-30 30 views

Trả lời

4

Tôi tin rằng tiêu chuẩn C++ không thực hiện bất kỳ đề cập nào về đa luồng - đa luồng là một tính năng dành riêng cho nền tảng.

Tôi không chắc chính xác tiêu chuẩn C++ nói gì về ngoại lệ chưa được nói chung, nhưng theo số this page, điều xảy ra là nền tảng được xác định và bạn nên tìm hiểu trong tài liệu của trình biên dịch.

Trong bài kiểm tra nhanh và bẩn tôi đã làm với g ++ 4.0.1 (i686-apple-darwin8-g ++ - 4.0.1 để cụ thể), kết quả là được gọi là terminate(), làm mất toàn bộ chương trình. Mã tôi đã sử dụng sau:

#include <stdio.h> 
#include <pthread.h> 

void *threadproc(void *x) 
{ 
    throw 0; 

    return NULL; 
} 

int main(int argc, char **argv) 
{ 
    pthread_t t; 
    pthread_create(&t, NULL, threadproc, NULL); 

    void *ret; 
    pthread_join(t, &ret); 

    printf("ret = 0x%08x\n", ret); 

    return 0; 
} 

Biên soạn với g++ threadtest.cc -lpthread -o threadtest. Đầu ra là:

terminate called after throwing an instance of 'int' 
+0

Tôi sẽ viết cùng một thứ. Chỉ cần làm cho nó rõ ràng hơn rằng các ứng dụng sẽ chấm dứt (ai đó có thể giải thích rằng chỉ là thread bị giết với chấm dứt). chấm dứt() sẽ làm cho tất cả các chuỗi bị giết mà không cần xử lý thêm. –

+0

PS. Đây là hành vi của tất cả các thư viện pthread mà tôi đã sử dụng. –

0

Tôi không khuyên bạn nên để bất kỳ ngoại lệ nào không bị bắt buộc. Bao bọc các hàm chuỗi cấp cao nhất của bạn trong các trình xử lý bắt tất cả có thể duyên dáng hơn (hoặc ít nhất là bằng lời) đóng chương trình.

+0

Mặc dù điều này * có thể * có ý nghĩa đối với một số ứng dụng mà tôi không đồng ý với nó như là lời khuyên chung. Thông thường, tôi khuyên mọi người nên xử lý các ngoại lệ khi nó có ý nghĩa, nếu không thì hãy để chúng lan truyền. Nếu bạn đã sử dụng RAII một cách chính xác thì không phải là vấn đề. – MattyT

+0

Ngoại trừ những kẻ hủy RAII của bạn có thể không được gọi nếu ngoại lệ không bị bắt. Đó là vào việc thực hiện cho dù một ngoại lệ uncaught thư giãn stack hay không. Nó chỉ có thể hủy bỏ. Nếu bạn bắt tất cả mọi thứ ở trên cùng, bạn đảm bảo thư giãn. –

+0

@onebyone: Theo đoạn văn nào trong thông số kỹ thuật? Cũng giống như để biết như tất cả các bậc thầy C++ (Herb Sutter vv) khuyên bạn nên viết mã mà không sử dụng try/catch nhưng thay vào đó là "ngoại lệ-thuyết bất khả tri". –

0

Tôi nghĩ điều quan trọng nhất là phải nhớ rằng các trường hợp ngoại lệ chưa được thực hiện từ các luồng khác không hiển thị cho người dùng hoặc được ném vào chuỗi chính. Vì vậy, bạn phải warp tất cả các mã nên chạy trên các chủ đề khác với chủ đề chính với các khối try/catch.

2

Trường hợp ngoại lệ chưa thực hiện sẽ gọi terminate() mà lần lượt gọi số terminate_handler (có thể được đặt bởi chương trình). Theo mặc định, terminate_handler gọi abort().

Ngay cả khi bạn ghi đè mặc định terminate_handler, tiêu chuẩn cho biết rằng thường trình bạn cung cấp "sẽ chấm dứt thực hiện chương trình mà không trả lại người gọi" (ISO 14882-2003 18.6.1.3).

Vì vậy, tóm lại, một ngoại lệ chưa bắt buộc sẽ chấm dứt chương trình không chỉ là chuỗi.

Theo như an toàn luồng đi, như Adam Rosenfield nói, đó là điều nền tảng cụ thể không được giải quyết theo tiêu chuẩn.

2

Đây là lý do lớn nhất duy nhất mà Erlang tồn tại.

Tôi không biết quy ước là gì, nhưng imho, càng giống Erlang càng tốt. Làm cho các đối tượng heap không thay đổi và thiết lập một số loại giao thức truyền thông điệp để giao tiếp giữa các luồng. Tránh khóa. Hãy chắc chắn rằng thông điệp vượt qua là ngoại lệ an toàn. Giữ càng nhiều thứ nhà nước trên ngăn xếp.

2

Như những người khác đã thảo luận, đồng thời (và thread-safety nói riêng) là một vấn đề kiến ​​trúc, ảnh hưởng đến cách bạn thiết kế hệ thống và ứng dụng của mình.

Nhưng tôi muốn đưa ra câu hỏi của bạn về sự căng thẳng giữa ngoại lệ an toàn và an toàn luồng.

Ở cấp độ an toàn luồng cấp yêu cầu thay đổi đối với giao diện. Cũng giống như ngoại lệ an toàn.Ví dụ: thông thường cho các lớp để trả về tham chiếu đến biến nội bộ, ví dụ:

class Foo { 
public: 
    void set_value(std::string const & s); 

    std::string const & value() const; 
}; 

Nếu Foo được chia sẻ bởi nhiều chủ đề, sự cố đang chờ bạn. Đương nhiên, bạn có thể đặt một mutex hoặc khóa khác để truy cập Foo. Nhưng ngay sau đó, tất cả các lập trình viên của C++ sẽ muốn đưa Foo vào một "ThreadSafeFoo". Gây tranh cãi của tôi, là giao diện cho Foo phải được thay đổi thành:

class Foo { 
public: 
    void set_value(std::string const & s); 

    std::string value() const; 
}; 

Có, nó đắt hơn, nhưng có thể làm an toàn chỉ với ổ khóa bên trong Foo. IMnsHO điều này tạo ra một số lượng nhất định của sự căng thẳng giữa thread-an toàn và ngoại lệ an toàn. Hoặc ít nhất, bạn cần thực hiện phân tích nhiều hơn vì mỗi lớp được sử dụng như một tài nguyên được chia sẻ cần được kiểm tra dưới cả hai đèn.

1

Có hai vấn đề tôi nhận thấy:

  • trong g ++ trên Linux, việc giết hại một sợi (pthread_cancel) được thực hiện bằng cách ném một "không rõ" ngoại lệ. Một mặt, cho phép bạn làm sạch độc đáo khi chuỗi bị giết. Mặt khác, nếu bạn bắt được ngoại lệ đó và không rethrow nó, mã của bạn kết thúc bằng abort(). Do đó, nếu bạn hoặc bất kỳ của các thư viện bạn sử dụng giết chủ đề, bạn có thể không có

    catch (...)

mà không

throw; 

trong mã ren của bạn. Here là tham chiếu đến hành vi này trên web:

  • Đôi khi bạn cần chuyển ngoại lệ giữa các chuỗi. Đây không phải là một điều dễ dàng để làm - chúng tôi đã kết thúc làm somehack, khi giải pháp thích hợp là loại marshalling/demarshalling bạn muốn sử dụng giữa các quá trình.
7

C++ 0x sẽ có Language Support for Transporting Exceptions between Threads để khi một chuỗi công nhân ném ra ngoại lệ, luồng sinh sản có thể bắt hoặc lấy lại nó.

Từ đề nghị:

namespace std { 

    typedef unspecified exception_ptr; 

    exception_ptr current_exception(); 
    void rethrow_exception(exception_ptr p); 

    template< class E > exception_ptr copy_exception(E e); 
} 
+2

Lưu ý rằng chức năng này đã tồn tại trong tăng cường. –

+1

Để biết thêm thông tin về việc triển khai Boost: http://www.boost.org/doc/libs/release/libs/exception/doc/tutorial_exception_ptr.html –

2

Một ví dụ điển hình (không thể nhớ nơi tôi nhìn thấy nó lần đầu tiên) là trong thư viện std.

Đây là cách bạn bật một cái gì đó từ một hàng đợi:

T t; 
t = q.front(); // may throw 
q.pop(); 

Giao diện này có phần tù so với:

T t = q.pop(); 

Nhưng được thực hiện vì việc chuyển nhượng bản sao T có thể ném. Nếu bản sao ném sau khi cửa sổ bật lên xảy ra, phần tử đó sẽ bị mất khỏi hàng đợi và không bao giờ có thể phục hồi được. Nhưng vì bản sao xảy ra trước khi phần tử được xuất hiện, bạn có thể đặt xử lý tùy ý xung quanh bản sao từ phía trước() trong các khối try/catch.

Điểm bất lợi là bạn không thể triển khai hàng đợi là luồng an toàn với giao diện std :: queue vì có hai bước liên quan. Điều gì là tốt cho an toàn ngoại lệ (tách ra các bước có thể ném), bây giờ là xấu cho đa luồng.

Vị cứu tinh chính của bạn trong an toàn ngoại lệ là hoạt động của con trỏ không được ném. Tương tự, các hoạt động con trỏ có thể được tạo ra trên hầu hết các nền tảng, vì vậy chúng thường có thể là vị cứu tinh của bạn trong mã đa luồng. Bạn có thể ăn bánh của bạn và ăn nó, nhưng nó thực sự là khó khăn.

+0

Tôi không hiểu cách bạn kết luận "bạn không thể triển khai hàng đợi là thread an toàn với std :: queue's interface vì hai bước liên quan ". –

+1

Hãy suy nghĩ về nó - ngay cả khi bạn khóa truy cập vào cấu trúc dữ liệu nội bộ, vì khóa đó không được giữ giữa các cuộc gọi đến trước và pop, sau khi bạn nhận được phần tử phía trước, một số chủ đề khác cũng có thể nhận được cùng phần tử trước đó bật nó ra, sau đó bạn mất một phần tử và một phần tử được nhân đôi. –

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