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修饰符等参数。

猜你喜欢

转载自blog.csdn.net/Fourier_Legend/article/details/82020686