2009-07-16 17 views
9

Tôi là nhà phát triển C++, người đã chủ yếu được lập trình trên Solaris và Linux cho đến gần đây, khi tôi bị buộc phải tạo một ứng dụng được nhắm mục tiêu đến Windows.Có cách nào để lấy/chèn luồng không khóa trên basic_iostream trong Windows không?

Tôi đã sử dụng thiết kế giao tiếp dựa trên luồng C/I/O được hỗ trợ bởi cổng TCP. Thiết kế dựa trên một luồng đơn đọc liên tục từ luồng (phần lớn thời gian bị chặn trong socket đọc chờ dữ liệu) trong khi các luồng khác gửi qua cùng một luồng (được đồng bộ hóa bởi mutex).

Khi di chuyển đến cửa sổ, tôi đã chọn sử dụng tăng :: asio :: ip :: tcp :: iostream để triển khai luồng socket. Tôi đã mất tinh thần khi thấy rằng thiết kế đa luồng trên dẫn đến bế tắc trên Windows. Có vẻ như operator<<(std::basic_ostream<...>,std::basic_string<...>) tuyên bố 'Đã gửi' khóa toàn bộ luồng cho cả hoạt động đầu vào và đầu ra. Kể từ khi thread đọc của tôi luôn luôn chờ đợi trên dòng, gửi các hoạt động từ các chủ đề khác bế tắc khi Sentry này được tạo ra.

Dưới đây là phần có liên quan của các cuộc gọi stack trong điều hành < < và Sentry xây dựng:

... 
    ntdll.dll!7c901046()  
    CAF.exe!_Mtxlock(_RTL_CRITICAL_SECTION * _Mtx=0x00397ad0) Line 45 C 
    CAF.exe!std::_Mutex::_Lock() Line 24 + 0xb bytes C++ 
    CAF.exe!std::basic_streambuf<char,std::char_traits<char> >::_Lock() Line 174 C++ 
    CAF.exe!std::basic_ostream<char,std::char_traits<char> >::_Sentry_base::_Sentry_base(std::basic_ostream<char,std::char_traits<char> > & _Ostr={...}) Line 78 C++ 
    CAF.exe!std::basic_ostream<char,std::char_traits<char> >::sentry::sentry(std::basic_ostream<char,std::char_traits<char> > & _Ostr={...}) Line 95 + 0x4e bytes C++ 
> CAF.exe!std::operator<<<char,std::char_traits<char>,std::allocator<char> >(std::basic_ostream<char,std::char_traits<char> > & _Ostr={...}, const std::basic_string<char,std::char_traits<char>,std::allocator<char> > & _Str="###") Line 549 + 0xc bytes C++ 
    ... 

tôi sẽ là tốt nếu istream và ostream thành phần bị nhốt riêng, nhưng đó không phải là trường hợp.

Có thực hiện thay thế các toán tử luồng mà tôi có thể sử dụng không? Tôi có thể trực tiếp không khóa? Tôi có nên thực hiện của riêng mình (không chắc chắn làm thế nào để làm điều này)?

Mọi đề xuất sẽ được đánh giá cao.

(Nền tảng là Windows 32 và hành vi 64-bit. Quan sát với Visual Studio 2003 Pro và 2008 Express)

+1

+1 Câu hỏi hay, được diễn đạt tốt. Đáng tiếc tôi không có câu trả lời cho bạn! –

Trả lời

1

Câu hỏi này đã biến mất đủ lâu. Tôi sẽ báo cáo những gì tôi đã làm ngay cả khi có một cơ hội tôi sẽ bị quấy rầy.

Tôi đã xác định rằng vấn đề là hai luồng đã đến bế tắc trong khi cố gắng truy cập một đối tượng iostream trong các hoạt động đọc và ghi riêng biệt. Tôi có thể thấy rằng việc thực hiện Visual Studio của chuỗi suối chèn và khai thác khai thác cả hai tuyên bố một Sentry, mà khóa bộ đệm dòng liên kết với các dòng đang được hoạt động trên.

Tôi biết rằng, đối với luồng được đề cập đến cho bế tắc này, việc triển khai bộ đệm luồng được tăng :: asio :: basic_socket_streambuf. Tôi kiểm tra việc thực hiện để thấy rằng các hoạt động đọc và ghi (tràn và tràn) thực sự hoạt động trên các bộ đệm khác nhau (so với đặt).

Với xác minh ở trên, tôi đã chọn chỉ đơn giản là phá vỡ khóa cho ứng dụng này. Để làm điều đó, tôi đã sử dụng các định nghĩa trước xử lý dự án cụ thể để loại trừ các mã khóa trong việc thực hiện basic_istream của sentry khóa:

class _Sentry_base 
     { // stores thread lock and reference to input stream 
    public: 
     __CLR_OR_THIS_CALL _Sentry_base(_Myt& _Istr) 
      : _Myistr(_Istr) 
      { // lock the stream buffer, if there 
#ifndef MY_PROJECT 
      if (_Myistr.rdbuf() != 0) 
       _Myistr.rdbuf()->_Lock(); 
#endif 
      } 

     __CLR_OR_THIS_CALL ~_Sentry_base() 
      { // destroy after unlocking 
#ifndef MY_PROJECT 
      if (_Myistr.rdbuf() != 0) 
       _Myistr.rdbuf()->_Unlock(); 
#endif 
      } 

Ưu điểm:

  • Nó hoạt động
  • Chỉ dự án của tôi (có định nghĩa phù hợp) bị ảnh hưởng

Nhược điểm:

  • Cảm thấy một chút hacky
  • Mỗi nền tảng nơi này được xây dựng sẽ cần phải sửa đổi này

tôi có kế hoạch để giảm thiểu các điểm sau bởi to tài liệu này trong mã và tài liệu dự án.

Tôi nhận ra rằng có thể có một giải pháp thanh lịch hơn cho vấn đề này, nhưng vì lợi ích của tính hiệu quả, tôi đã chọn giải pháp trực tiếp sau sự tích cực để hiểu tác động.

0

lẽ bạn có thể thực hiện một khóa lớp mình? I.E., có istreamostream riêng mà bạn tự khóa khi được gọi. Định kỳ, kiểm tra xem cả hai được mở khóa, và sau đó đọc từ một trong những khác.

0

Bạn đã xóa luồng rõ ràng sau khi ghi vào luồng chưa? This blog post ngụ ý rằng dữ liệu của bạn có thể bị "kẹt" trong bộ đệm. Nếu đó là sự thật, thì có lẽ bạn xuất hiện bế tắc vì không có gì để đọc. Thêm stream << std::flush vào cuối hoạt động gửi của bạn.

Một thay thế (mặc dù kém hiệu quả) giải pháp được đề xuất bởi các bài viết trên blog là để tắt đệm đầu ra của dòng:

stream.rdbuf()->pubsetbuf(0, 0); 
+0

Tôi nghĩ rằng ngăn xếp cuộc gọi của mình cho thấy một vấn đề khóa của nó. –

+0

Tôi thực sự làm tuôn ra luồng ở ranh giới thư, nhưng đó không phải là vấn đề ở đây. Đầu kia của kết nối này không bị kẹt chờ dữ liệu. Thay vào đó, hai chủ đề bị bế tắc trên 'sentry' của iostream mà các cửa sổ stream xác định. Cảm ơn bạn đã cân nhắc. –

1

Theo các tài liệu hướng tăng [1] việc sử dụng hai đề truy cập vào một đối tượng mà không có mutexes là "không an toàn". Chỉ vì nó làm việc trên nền tảng Unix là không đảm bảo rằng nó sẽ làm việc trên nền tảng Windows.

Vì vậy, lựa chọn của bạn là:

  1. Viết lại mã của bạn để đề của bạn không truy cập vào các đối tượng đồng thời
  2. Patch thư viện boost và gửi những thay đổi trở lại
  3. Hỏi Chris thực sự độc đáo nếu anh ta sẽ làm thay đổi cho nền tảng Windows

[1] http://www.boost.org/doc/libs/1_39_0/doc/html/boost_asio/overview/core/threads.html

+0

Cảm ơn bạn đã nhập. Chỉ cần làm rõ - câu hỏi này liên quan cụ thể đến việc thực hiện Visual Studio của mẫu basic_ostream, mở rộng lớp khóa và kiểm tra lỗi được gọi là Sentry. Đây không phải là một vấn đề với boost :: asio library (mặc dù tài liệu tham khảo của bạn là một điều tốt để lưu ý). –

+0

Bạn có cần chuyển đổi từ tcp :: iostream sang Visual Studio's std :: iostream không? Bạn có thể có thêm may mắn nếu bạn giữ nó tcp :: iostream tất cả các cách. – teambob

+0

Đã không nghĩ rằng thông qua tất cả các cách ... Tôi thường viết lớp của tôi vào/ra (đọc/ghi, vv) về mẫu số chung thấp nhất (istream/ostream). Cảm ơn các thực phẩm cho các tư tưởng. –

0

Tôi biết đây là một câu hỏi cũ ... nhưng tôi đã phải tự mình làm điều này!

tình hình của tôi là phức tạp hơn vì đây là streambuf của riêng tôi, nhưng bạn có thể khắc phục điều này bằng cách thực hiện:

std::ostream &operator<<(std::ostream &x, std::string &y) 
{ 
    x.rdbuf()->_Unlock(); 
    x << y.c_str(); 
} 

mà được gọi là ưu tiên của std :: phiên bản.

Bạn dĩ nhiên phải làm điều này cho mọi nhà điều hành Windows < < gọi _Lock và (trong trường hợp của tôi) tất cả các cuộc gọi đọc/ghi của bạn trong streambuf.

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