C++ 菜鳥之路 (四) boost::thread 多線程全解析

官方解釋: http://www.cplusplus.com/reference/thread/thread/joinable/

boost::thread 的一般用法

/*
thread.cpp
*/
#include <boost/thread/thread.hpp> 
#include <iostream> 

#include <unistd.h>

/* 
 * g++ thread.cpp -lboost_system -lboost_thread
*/

void helloA() 
{ 
        std::cout << "I'm thread A !  --- Start " << std::endl; 

        sleep(10);

        std::cout << "I'm thread A !  --- OVER " << std::endl; 

} 


  void helloB() 
{ 
        std::cout << "I'm thread B !  --- Start " << std::endl; 

        sleep(10);

        std::cout << "I'm thread B !  --- OVER " << std::endl; 

} 

int main(int argc, char* argv[]) 
{ 
        boost::thread thrdA(&helloA);
        boost::thread thrdB(&helloB);  
        thrdA.join();
        thrdB.join(); 
        // join() 的作用是讓主進程等待子線程執行完畢後再繼續執行。
        // 否則會不檢查子線程執行狀況,直接退出了。
        return 0; 
} 


/*結果
I'm thread A !  --- Start 
I'm thread B !  --- Start 
I'm thread A !  --- OVER 
I'm thread B !  --- OVER 
*/

編譯方式:

g++ thread.cpp -lboost_system -lboost_thread

如果直接採用 g++ thread.cpp 會出現如下的錯誤,

/tmp/ccoutPZ3.o: In function `__static_initialization_and_destruction_0(int, int)':
thread.cpp:(.text+0xce): undefined reference to `boost::system::generic_category()'
thread.cpp:(.text+0xda): undefined reference to `boost::system::generic_category()'
thread.cpp:(.text+0xe6): undefined reference to `boost::system::system_category()'

參考這個回答:
https://stackoverflow.com/questions/13467072/c-boost-undefined-reference-to-boostsystemgeneric-category 是因爲這裏找不到鏈接庫, 所以需要 -lboost_system 和 -lboost_thread

boost::thread的幾個函數

函數 功能
join() 讓主進程等待子線程執行完畢後再繼續執行
get_id() 獲得線程的 id 號
detach() 標線程就成爲了守護線程,駐留後臺運行
bool joinable() 是否爲 join()

thread::join()是個簡單暴力的方法,主線程等待子進程期間什麼都不能做,一般情形是主線程創建thread object後做自己的工作而不是簡單停留在join上。
thread::join()還會清理子線程相關的內存空間,此後thread object將不再和這個子線程相關了,即thread object不再joinable了,所以join對於一個子線程來說只可以被調用一次,爲了實現更精細的線程等待機制,可以使用條件變量等機制。

1、可會合(joinable):這種關係下,主線程需要明確執行等待操作,在子線程結束後,主線程的等待操作執行完畢,子線程和主線程會合,這時主線程繼續執行等待操作之後的下一步操作。主線程必須會合可會合的子線程。在主線程的線程函數內部調用子線程對象的wait函數實現,即使子線程能夠在主線程之前執行完畢,進入終止態,也必須執行會合操作,否則,系統永遠不會主動銷燬線程,分配給該線程的系統資源也永遠不會釋放。

2、相分離(detached):表示子線程無需和主線程會合,也就是相分離的,這種情況下,子線程一旦進入終止狀態,這種方式常用在線程數較多的情況下,有時讓主線程逐個等待子線程結束,或者讓主線程安排每個子線程結束的等待順序,是很困難或不可能的,所以在併發子線程較多的情況下,這種方式也會經常使用。

鎖 - lock ( ) 函數

#include <boost/thread/thread.hpp> 
#include <boost/thread/mutex.hpp> //定義鎖
#include <iostream> 

#include <unistd.h>

/* 
 * g++ thread.cpp -lboost_system -lboost_thread
*/

//boost::shared_mutex  read_write_mutex;
boost::mutex lock; //使用的鎖

using namespace std;
int num = 100;
void helloA() 
{ 
        std::cout << "I'm thread A ! " << boost::this_thread::get_id()  << " --- Start " << std::endl; 

        lock.lock(); // 鎖住變量 num, 另一處調用將在此處運行完後再繼續運行

        num++;

        std::cout << num <<std::endl;

        sleep(3);

        lock.unlock();

        std::cout << "I'm thread A !  --- OVER " << std::endl; 

} 


  void helloB() 
{ 
        std::cout << "I'm thread B ! " << boost::this_thread::get_id()  << " --- Start " << std::endl; 

        lock.lock(); 

        num++;

        std::cout << num <<std::endl;

        sleep(3);

        lock.unlock();

        std::cout << "I'm thread B !  --- OVER " << std::endl; 

} 

int main(int argc, char* argv[]) 
{ 
        // 建立並執行兩個線程

        boost::thread thrdA(&helloA);


        boost::thread thrdB(&helloB);  


        thrdA.join(); // 等待子線程完成後再繼續執行主進程;


        thrdB.join();


        // 等待兩個 join 後纔會繼續執行
        cout<< " ==== over ==== "<<endl;

        return 0; 
} 

/*結果
I'm thread A ! 7f5792ddc700 --- Start 
101 
I'm thread B ! 7f57925db700 --- Start 
I'm thread A !  --- OVER 
102
I'm thread B !  --- OVER 
 ==== over ====

數據 num 被鎖住, 線程B中的 num 將不能修該。 所以線程A 執行完才能修改 num 的值, 才能執行線程B。
*/

多線程函數的限制

工作的時候, 想讓發佈地圖單獨佔用一個線程, 但是發現一些問題。

    boost::thread thrdA(&updateMap)
    thrdA.join();
    /*
    if(updateMap())
    {
        ROS_DEBUG("Updated the map");
        }
    */
    return;

編譯的時候出現如下的錯誤

error: ISO C++ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to member function.  Say ‘&SlamKarto::updateMap’ [-fpermissive]
  boost::thread thrdA(&updateMap);
                       ^
In file included from /usr/include/boost/thread/thread_only.hpp:22:0,
                 from /usr/include/boost/thread/thread.hpp:12,
                 from /usr/include/boost/thread.hpp:13,
                 from /opt/ros/indigo/include/tf/transform_listener.h:43,
                 from /home/moi/SLAM_Gmapping/src/slam_karto/src/slam_karto.cpp:31:
/usr/include/boost/thread/detail/thread.hpp: In instantiation of ‘void boost::detail::thread_data<F>::run() [with F = bool (SlamKarto::*)()]’:

大意錯誤就是, 多線程函數不能是非靜態成員函數, 於是我再各個函數上加了 static bool SlamKarto::updateMap( ) 但是編譯再次出錯, 還是很傻的錯誤。

error: invalid use of member ‘SlamKarto::map_mutex_’ in static member function
   boost::mutex map_mutex_;
// 原函數包含太多的非靜態成員
(1)普通數據成員屬於類的一個具體的對象,只有對象被創建了,普通數據成員纔會被分配內存。而靜態數據成員屬於整個類,即使沒有任何對象創建,類的靜態數據成員變量也存在。
(2)因爲類的靜態數據成員的存在不依賴與於任何類對象的存在,類的靜態數據成員應該在代碼中被顯式地初始化,一般要在類外進行,例如上例。在C++11標準中,我們可以爲靜態成員提供const整數類型的類內初始值,不過要求靜態成員必須是字面值常量類型的constexpr(源自C++Primer中文版270頁)。
(3)外部訪問類的靜態成員能直接通過類名來訪問,例如:test::getCount()。雖然靜態成員不屬於類的某個對象,但是我們仍然可以使用類的對象、引用或指針來訪問靜態成員(源自C++Primer中文版269頁),例如:
test ac1,*ac2;
int r;
r=ac1.getCount();// 或者 r=ac2->getCount();
(4)類的靜態成員函數無法直接訪問普通數據成員(可以通過對象名間接的訪問),而類的任何成員函數都可以訪問類的靜態數據成員。
(5)靜態成員和類的普通成員一樣,也具有public、protected、private3種訪問級別,也可以具有返回值、const修飾符等參數。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章