2014-09-27 30 views
2

我一直在通過學習使用boost的基本串行終端示例來實現半雙工串行驅動程序: ASIO :: basic_serial_port: http://lists.boost.org/boost-users/att-41140/minicom.cpp回調傳遞給boost :: asio :: async_read_some在boost :: asio :: read_some返回數據時從未調用

我需要異步讀取,但仍然會檢測時,處理程序在主線程中完成,所以我使用boost通過async_read_some與幾個額外的參考參數回調的lambda函數:綁定。處理程序永遠不會被調用,但如果我用read_some函數替換async_read_some函數,它將返回沒有問題的數據。

我相信我全部滿足此功能的必然要求調用處理程序,因爲它們對ASIO同一::看了一些函數返回:

  1. 緩衝區保持在範圍
  2. 一個或多個字節是由串行設備
  3. 在IO服務正在運行
  4. 端口是開放的,在運行正確的波特率
接收

有沒有人知道我是否錯過了異步讀取的另一個獨特假設,或者如果我沒有正確設置io_service?

下面是我如何使用與async_read_some(http://www.boost.org/doc/libs/1_56_0/doc/html/boost_asio/reference/basic_serial_port/async_read_some.html)的代碼示例:

void readCallback(const boost::system::error_code& error, size_t bytes_transfered, bool & finished_reading, boost::system::error_code& error_report, size_t & bytes_read) 
{ 
    std::cout << "READ CALLBACK\n"; 
    std::cout.flush(); 
    error_report = error; 
    bytes_read = bytes_transfered; 
    finished_reading = true; 
    return; 
} 

int main() 
{ 
    int baud_rate = 115200; 
    std::string port_name = "/dev/ttyUSB0"; 
    boost::asio::io_service io_service_; 
    boost::asio::serial_port serial_port_(io_service_,port_name); 
    serial_port_.set_option(boost::asio::serial_port_base::baud_rate(baud_rate)); 
    boost::thread service_thread_; 
    service_thread = boost::thread(boost::bind(&boost::asio::io_service::run, &io_service_)); 

    std::cout << "Starting byte read\n"; 
    boost::system::error_code ec; 

    bool finished_reading = false; 
    size_t bytes_read; 
    int max_response_size = 8; 
    uint8_t read_buffer[max_response_size]; 
    serial_port_.async_read_some(boost::asio::buffer(read_buffer, max_response_size), 
        boost::bind(readCallback, 
           boost::asio::placeholders::error, 
           boost::asio::placeholders::bytes_transferred, 
           finished_reading, ec, bytes_read)); 

    std::cout << "Waiting for read to finish\n"; 
    while (!finished_reading) 
    { 
     boost::this_thread::sleep(boost::posix_time::milliseconds(1)); 
    } 

    std::cout << "Finished byte read: " << bytes_read << "\n"; 

    for (int i = 0; i < bytes_read; ++i) 
    { 
    printf("0x%x ",read_buffer[i]); 
    } 
} 

結果是回調不打印出任何東西和同時完成循環永遠不會結束!

這裏是我如何使用阻塞read_some功能(boost.org/doc/libs/1_56_0/doc/html/boost_asio/reference/basic_serial_port/read_some.html):從

int main() 
{ 
    int baud_rate = 115200; 
    std::string port_name = "/dev/ttyUSB0"; 
    boost::asio::io_service io_service_; 
    boost::asio::serial_port serial_port_(io_service_,port_name); 
    serial_port_.set_option(boost::asio::serial_port_base::baud_rate(baud_rate)); 
    boost::thread service_thread_; 
    service_thread = boost::thread(boost::bind(&boost::asio::io_service::run, &io_service_)); 

    std::cout << "Starting byte read\n"; 
    boost::system::error_code ec; 
    int max_response_size = 8; 
    uint8_t read_buffer[max_response_size]; 
    int bytes_read = serial_port_.read_some(boost::asio::buffer(read_buffer, max_response_size),ec); 

    std::cout << "Finished byte read: " << bytes_read << "\n"; 

    for (int i = 0; i < bytes_read; ++i) 
    { 
    printf("0x%x ",read_buffer[i]); 
    } 
} 

這個版本打印1發送最多8個字符,阻止發送至少一個字符。

回答

1

該代碼不能保證io_service正在運行。 io_service::run()將返回當任:

  • 所有的工作已經完成,並沒有更多的處理程序被分派
  • io_service已經停止。

在這種情況下,有可能要創建的service_thread_,並啓動serial_port::async_read_some()操作之前調用io_service::run(),增加工作的io_service。因此,service_thread_可以立即從io_service::run()返回。要解決此問題,請執行以下任一操作:

  • 異步操作啓動後調用io_service::run()
  • 在開始service_thread_之前創建一個io_service::work對象。 A work對象可防止io_service耗盡工作。

answer可能會提供一些更深入瞭解io_service::run()的行爲。


其他一些事情要注意,並在Igor's answer擴大:

  • 如果一個線程沒有以有意義的方式前進等待異步操作完成(即紡循環睡眠),那麼可能值得研究一下混合同步行爲與異步操作是否是正確的解決方案。
  • boost::bind()按值複製參數。通過參考參數,與boost::ref()boost::cref()把它包:

    boost::bind(..., boost::ref(finished_reading), boost::ref(ec), 
          boost::ref(bytes_read)); 
    
  • 同步需要被添加,以保證在主線程的finished_reading存儲器可視性。對於異步操作,Boost.Asio將​​作爲合適的內存屏障來確保正確的內存可見性(有關更多詳細信息,請參閱此answer)。在這種情況下,主線程中需要memory barrier以保證主線程觀察其他線程對finished_reading的更改。考慮使用Boost.Thread同步機制,如boost::mutex或Boost.Atomic的atomic objectsthread and signal fences

+0

感謝您的回答,它解決了我的示例中的所有問題。關於io_service :: run行爲的答案的鏈接非常具有說明性。這僅僅是一個測試代碼,我的實際代碼基於紡紗循環中主線程作用域中的數據包解析細節實現了一個超時。如果數據進入半雙工總線並且本質上是同步的(只要沒有出現任何問題),使用異步讀取的同步結構是合理的,因此我可以確保超時並清除混亂讀取失敗? – st3am 2014-09-29 02:51:33

+0

我想我明白你在重新檢查同步和異步行爲的用法。看來我應該鏈接異步調用來填充一個緩衝區,然後在發送請求後在同步線程中讀取它。 – st3am 2014-09-29 21:37:38

+0

@ st3am如果您需要超時,則需要異步操作。但是,使用其他線程可能會使解決方案複雜化。考慮檢查官方Boost.Asio [超時示例]中使用的超時方法(http://www.boost.org/doc/libs/1_56_0/doc/html/boost_asio/examples/cpp03_examples.html#boost_asio.examples.cpp03_examples .timeouts)。對於半雙工系統,通常可以使用單個線程等待讀取完成或超時,然後響應並重復。 – 2014-09-30 02:06:40

1

請注意boost::bindcopies its arguments。如果你想通過引用傳遞參數,用boost::ref(或std::ref)把它包:

boost::bind(readCallback, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred, boost::ref(finished_reading), ec, bytes_read)); 

(不過,嚴格來說,有您傳遞到另一個線程bool可變的競爭條件更好的解決方案。請使用std::atomic_bool。)

+0

感謝您的回答,當運行事件尚未返回時,使用ref包裝允許修改回調參數。 – st3am 2014-09-29 02:57:03

相關問題