Boost 线程相关介绍

相关文章:
boost::thread简要分析(1):thread
http://www.rosoo.net/a/cpp/2009/1204/8082.html
boost::thread简要分析(2):线程同步
http://www.rosoo.net/a/cpp/2009/1204/8083.html
boost::thread简要分析(3):线程局部存储及其它
http://www.rosoo.net/a/cpp/2009/1204/8084.html

昨天在写作“大卫的Design Patterns学习笔记”过程中,编写了一个多线程Producer- Consumer的示例,其中用到了boost:: thread,但在线程同步的问题上遇到了些问题,到csdn和vckbase上发帖子问了一下,也没人回答,没有办法,只好晚上回家搬出源码研究了一下,总算解决了问题,下面将自己的理解写下来,与大家分享、讨论。
注:以下讨论基于boost1.32.0 。

boost:: thread库跟boost:: function等很多其它boost组成库不同,它只是一个跨平台封装库(简单的说,就是根据不同的宏调用不同的API),里面没有太多的GP编程技巧,因此,研究起来比较简单。

boost:: thread库主要由以下部分组成:
thread
mutex
scoped_lock
condition
xtime
barrier
下面依次解析如下:

thread
thread自然是boost:: thread 库的主角,但thread类的实现总体上是比较简单的,前面已经说过,thread只是一个跨平台的线程封装库,其中按照所使用的编译选项的不同,分别决定使用Windows线程API还是pthread,或者Macintosh Carbon平台的thread实现。以下只讨论Windows,即使用 BOOST_HAS_WINTHREADS的情况。
thread类提供了两种构造函数:
thread:: thread()
thread:: thread( const  function0< void >&  threadfunc)

第一种构造函数用于调用GetCurrentThread构造一个当前线程的thread对象,第二种则通过传入一个函数或者一个functor来创建一个新的线程。第二种情况下,thread类在其构造函数中间接调用CreateThread来创建线程,并将线程句柄保存到成员变量m_thread中,并执行传入的函数,或执行functor的operator  () 方法来启动工作线程。
此外,thread类有一个Windows下的程序员可能不大熟悉的成员函数join,线程(通常是主线程)可以通过调用join函数来等待另一线程(通常是工作线程)退出,join的实现也十分简单,是调用WaitForSingleObject来实现的:
WaitForSingleObject( reinterpret_cast < HANDLE>( m_thread),  INFINITE);
我们可以用以下三种方式启动一个新线程:
1 、传递一个工作函数来构造一个工作线程
#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <iostream>

boost:: mutex io_mutex;

void  count()     // worker function
{
    for  ( int  i =  0 ;  i <  10 ; ++ i)
    {
        boost:: mutex:: scoped_lock lock( io_mutex);
        std:: cout <<  i <<  std:: endl;
    }
}

int  main ( int  argc,  char *  argv[])
{
    boost:: thread thrd1(& count);
    boost:: thread thrd2(& count);
    thrd1. join();
    thrd2. join();

    return  0 ;
}

2 、传递一个functor对象来构造一个工作线程
#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <iostream>

boost:: mutex io_mutex;

struct  count
{
    count( int  id) :  id( id) { }

    void  operator ()()
    {
        for  ( int  i =  0 ;  i <  10 ; ++ i)
        {
            boost:: mutex:: scoped_lock lock( io_mutex);         // lock io, will be explained soon.
            std:: cout <<  id <<  ": "  <<  i <<  std:: endl;
        }
    }

    int  id;
};

int  main ( int  argc,  char *  argv[])
{
    boost:: thread thrd1( count( 1 ));
    boost:: thread thrd2( count( 2 ));
    thrd1. join();
    thrd2. join();
    return  0 ;
}

3 、无需将类设计成一个functor,借助bind来构造functor对象以创建工作线程
#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/bind.hpp>
#include <iostream>

boost:: mutex io_mutex;

struct  count
{
    static  int  num;
    int  id;

    count() :  id( num++) {}

    int  do_count( int  n)
    {
        for  ( int  i =  0 ;  i <  n; ++ i)
        {
            boost:: mutex:: scoped_lock lock( io_mutex);
            std:: cout <<  id <<  ": "  <<  i <<  std:: endl;
        }
        return  id;
    }
};

int  count:: num =  1 ;

int  main ( int  argc,  char *  argv[])
{
    count c1;
    boost:: thread thrd1( boost:: bind(& count:: do_count, & c1,  10 ));
    thrd1. join();
    return  0 ;
}

其中bind是一个函数模板,它可以根据后面的实例化参数构造出一个functor来,上面的boost:: bind(& count:: do_count, & c1,  10 ) 其实等价于返回了一个functor:
struct  countFunctor
{
    int  operator () ()
    {
        (& c1)-> do_count( 10 );     // just a hint, not actual code
    }
};

因此,以后就跟2 中是一样的了。

原文:http://blog.vckbase.com/billdavid/archive/2005/05/19/5566.html

 

boost::thread简要分析(1):thread
http://www.rosoo.net/a/cpp/2009/1204/8082.html
boost::thread简要分析(2):线程同步
http://www.rosoo.net/a/cpp/2009/1204/8083.html
boost::thread简要分析(3):线程局部存储及其它
http://www.rosoo.net/a/cpp/2009/1204/8084.html

除了thread,boost::thread另一个重要组成部分是mutex,以及工作在mutex上的boost::mutex::scoped_lock、condition和barrier,这些都是为实现线程同步提供的。

mutex
boost提供的mutex有6种:
boost::mutex
boost::try_mutex
boost::timed_mutex
boost::recursive_mutex
boost::recursive_try_mutex
boost::recursive_timed_mutex
下面仅对boost::mutex进行分析。
mutex类是一个CriticalSection(临界区)封装类,它在构造函数中新建一个临界区并InitializeCriticalSection,然后用一个成员变量
void* m_mutex;
来保存该临界区结构。
除此之外,mutex还提供了do_lock、do_unlock等方法,这些方法分别调用EnterCriticalSection、LeaveCriticalSection来修改成员变量m_mutex(CRITICAL_SECTION结构指针)的状态,但这些方法都是private的,以防止我们直接对mutex进行锁操作,所有的锁操作都必须通过mutex的友元类detail::thread::lock_ops<mutex>来完成,比较有意思的是,lock_ops的所有方法:lock、unlock、trylock等都是static的,如lock_ops<Mutex>::lock的实现:

 
 
  1. template <typename Mutex>  
  2. class lock_ops : private noncopyable  
  3. {  
  4. ...  
  5. public:  
  6.     static void lock(Mutex& m)  
  7.     {  
  8.         m.do_lock();  
  9.     }  
  10. ...  

boost::thread的设计者为什么会这么设计呢?我想大概是:
1、boost::thread的设计者不希望被我们直接操作mutex,改变其状态,所以mutex的所有方法都是private的(除了构造函数,析构函数)。
2、虽然我们可以通过lock_ops来修改mutex的状态,如:

 
 
  1. #include <boost/thread/thread.hpp>  
  2. #include <boost/thread/mutex.hpp>  
  3. #include <boost/thread/detail/lock.hpp>  
  4.  
  5. int main()  
  6. {  
  7.     boost::mutex mt;  
  8.     //mt.do_lock();        // Error! Can not access private member!  
  9.  
  10.     boost::detail::thread::lock_ops<boost::mutex>::lock(mt);  
  11.  
  12.     return 0;  
  13. }  

但是,这是不推荐的,因为mutex、scoped_lock、condition、barrier是一套完整的类系,它们是相互协同工作的,像上面这么操作没有办法与后面的几个类协同工作。

scoped_lock
上面说过,不应该直接用lock_ops来操作mutex对象,那么,应该用什么呢?答案就是scoped_lock。与存在多种mutex一样,存在多种与mutex对应的scoped_lock:
scoped_lock
scoped_try_lock
scoped_timed_lock
这里我们只讨论scoped_lock。
scoped_lock是定义在namespace boost::detail::thread下的,为了方便我们使用(也为了方便设计者),mutex使用了下面的typedef:
typedef detail::thread::scoped_lock<mutex> scoped_lock;
这样我们就可以通过:
boost::mutex::scoped_lock
来使用scoped_lock类模板了。
由于scoped_lock的作用仅在于对mutex加锁/解锁(即使mutex EnterCriticalSection/LeaveCriticalSection),因此,它的接口也很简单,除了构造函数外,仅有lock/unlock/locked(判断是否已加锁),及类型转换操作符void*,一般我们不需要显式调用这些方法,因为scoped_lock的构造函数是这样定义的:

 
 
  1. explicit scoped_lock(Mutex& mx, bool initially_locked=true)  
  2.     : m_mutex(mx), m_locked(false)  
  3. {  
  4.     if (initially_locked) lock();  


注:m_mutex是一个mutex的引用。
因此,当我们不指定initially_locked参数构造一个scoped_lock对象时,scoped_lock会自动对所绑定的mutex加锁,而析构函数会检查是否加锁,若已加锁,则解锁;当然,有些情况下,我们可能不需要构造时自动加锁,这样就需要自己调用lock方法。后面的condition、barrier也会调用scoped_lock的lock、unlock方法来实现部分方法。
正因为scoped_lock具有可在构造时加锁,析构时解锁的特性,我们经常会使用局部变量来实现对mutex的独占访问。如thread部分独占访问cout的例子:

 
 
  1. #include <boost/thread/thread.hpp>  
  2. #include <boost/thread/mutex.hpp>  
  3. #include <iostream>  
  4.  
  5. boost::mutex io_mutex;  
  6.  
  7. void count()    // worker function  
  8. {  
  9.     for (int i = 0; i < 10; ++i)  
  10.     {  
  11.         boost::mutex::scoped_lock lock(io_mutex);  
  12.         std::cout << i << std::endl;  
  13.     }  
  14. }  
  15.  
  16. int main(int argc, char* argv[])  
  17. {  
  18.     boost::thread thrd1(&count);  
  19.     boost::thread thrd2(&count);  
  20.     thrd1.join();  
  21.     thrd2.join();  
  22.  
  23.     return 0;  
  24. }  


在每次输出信息时,为了防止整个输出过程被其它线程打乱,通过对io_mutex加锁(进入临界区),从而保证了输出的正确性。
在使用scoped_lock时,我们有时候需要使用全局锁(定义一个全局mutex,当需要独占访问全局资源时,以该全局mutex为参数构造一个scoped_lock对象即可。全局mutex可以是全局变量,也可以是类的静态方法等),有时候则需要使用对象锁(将mutex定义成类的成员变量),应该根据需要进行合理选择。
Java的synchronized可用于对方法加锁,对代码段加锁,对对象加锁,对类加锁(仍然是对象级的),这几种加锁方式都可以通过上面讲的对象锁来模拟;相反,在Java中实现全局锁好像有点麻烦,必须将请求封装到类中,以转换成上面的四种synchronized形式之一。

condition
condition的接口如下:

 
 
  1. class condition : private boost::noncopyable  
  2. // Exposition only  
  3. {  
  4. public:  
  5.   // construct/copy/destruct  
  6.   condition();  
  7.   ~condition();  
  8.  
  9.   // notification  
  10.   void notify_one();  
  11.   void notify_all();  
  12.  
  13.   // waiting  
  14.   template<typename ScopedLock> void wait(ScopedLock&);  
  15.   template<typename ScopedLock, typename Pred> void wait(ScopedLock&, Pred);  
  16.   template<typename ScopedLock>  
  17.     bool timed_wait(ScopedLock&, const boost::xtime&);  
  18.   template<typename ScopedLock, typename Pred>  
  19.     bool timed_wait(ScopedLock&, Pred);  
  20. };  

其中wait用于等待某个condition的发生,而timed_wait则提供具有超时的wait功能,notify_one用于唤醒一个等待该condition发生的线程,notify_all则用于唤醒所有等待该condition发生的线程。

由于condition的语义相对较为复杂,它的实现也是整个boost::thread库中最复杂的(对Windows版本而言,对支持pthread的版本而言,由于pthread已经提供了pthread_cond_t,使得condition实现起来也十分简单),下面对wait和notify_one进行简要分析。
condition内部包含了一个condition_impl对象,由该对象执行来处理实际的wait、notify_one...等操作。

下面先对condition_impl进行简要分析。
condition_impl在其构造函数中会创建两个Semaphore(信号量):m_gate、m_queue,及一个Mutex(互斥体,跟boost::mutex类似,但boost::mutex是基于CriticalSection<临界区>的):m_mutex,其中:
m_queue
相当于当前所有等待线程的等待队列,构造函数中调用CreateSemaphore来创建Semaphore时,lMaximumCount参数被指定为(std::numeric_limits<long>::max)(),即便如此,condition的实现者为了防止出现大量等待线程的情况(以至于超过了long的最大值),在线程因执行condition::wait进入等待状态时会先:
WaitForSingleObject(reinterpret_cast<HANDLE>(m_queue), INFINITE);
以等待被唤醒,但很难想象什么样的应用需要处理这么多线程。
m_mutex
用于内部同步的控制。
但对于m_gate我很奇怪,我仔细研究了一下condition_imp的实现,还是不明白作者引入m_gate这个变量的用意何在,既然已经有了用于同步控制的m_mutex,再引入一个m_gate实在让我有点不解。

以下是condition::wait调用的do_wait方法简化后的代码:

 
 
  1. template <typename M>  
  2. void do_wait(M& mutex)  
  3. {  
  4.     m_impl.enter_wait();  
  5.     lock_ops::unlock(mutex, state);    //对传入的scoped_lock对象解锁,以便别的线程可以  
  6. //对其进行加锁,并执行某些处理,否则,本线程等待的condition永  
  7. //远不会发生(因为没有线程可以获得访问资源的权利以使condition发生)  
  8.     m_impl.do_wait();    //执行等待操作,等待其它线程执行notify_one或notify_all操作以获得  
  9.     lock_ops::lock(mutex, state);    //重新对scoped_lock对象加锁,获得独占访问资源的权利  

condition::timed_wait的实现方法与此类似,而notify_one、notify_all仅将调用请求转发给m_impl,就不多讲了。

虽然condition的内部实现比较复杂,但使用起来还是比较方便的。下面是一个使用condition的多Producer-多Consumer同步的例子(这是本人为即将推出的“大卫的Design Patterns学习笔记”编写的Mediator模式的示例):
 

 
 
  1. #include <boost/thread/thread.hpp>  
  2. #include <boost/thread/mutex.hpp>  
  3. #include <boost/thread/condition.hpp>  
  4. #include <boost/thread/xtime.hpp>  
  5.  
  6. #include <iostream>  
  7. #include <time.h> // for time()  
  8.  
  9. #include <Windows.h>    
  10. // for Sleep, change it for other platform, we can use  
  11. // boost::thread::sleep, but it's too inconvenient.  
  12.  
  13. typedef boost::mutex::scoped_lock scoped_lock;  
  14. boost::mutex io_mutex;  
  15.  
  16. class Product  
  17. {  
  18.     int num;  
  19. public:  
  20.     Product(int num) : num(num) {}  
  21.  
  22.     friend std::ostream& operator<< (std::ostream& os, Product& product)  
  23.     {  
  24.         return os << product.num;  
  25.     }  
  26. };  
  27.  
  28. class Mediator  
  29. {  
  30. private:  
  31.     boost::condition cond;  
  32.     boost::mutex mutex;  
  33.  
  34.     Product** pSlot;    // product buffer/slot  
  35.     unsigned int slotCount,    // buffer size  
  36.         productCount; // current product count  
  37.     bool stopFlag;    // should all thread stop or not  
  38.  
  39. public:  
  40.     Mediator(const int slotCount) : slotCount(slotCount)
  41. , stopFlag(false), productCount(0)  
  42.     {  
  43.         pSlot = new Product*[slotCount];  
  44.     }  
  45.  
  46.     virtual ~Mediator()  
  47.     {  
  48.         for (int i = 0; i < static_cast<int>(productCount); i++)  
  49.         {  
  50.             delete pSlot[i];  
  51.         }  
  52.         delete [] pSlot;  
  53.     }  
  54.  
  55.     bool Stop() const { return stopFlag; }  
  56.     void Stop(bool) { stopFlag = true; }  
  57.  
  58.     void NotifyAll()    // notify all blocked thread to exit  
  59.     {  
  60.         cond.notify_all();  
  61.     }  
  62.  
  63.     bool Put( Product* pProduct)  
  64.     {  
  65.         scoped_lock lock(mutex);  
  66.         if (productCount == slotCount)  
  67.         {  
  68.             {  
  69.                 scoped_lock lock(io_mutex);  
  70.                 std::cout << "Buffer is full. Waiting..." << std::endl;  
  71.             }  
  72.             while (!stopFlag && (productCount == slotCount))  
  73.                 cond.wait(lock);  
  74.         }  
  75.         if (stopFlag) // it may be notified by main thread to quit.  
  76.             return false;  
  77.  
  78.         pSlot[ productCount++ ] = pProduct;  
  79.         cond.notify_one();    // this call may cause *pProduct
  80. // to be changed if it wakes up a consumer  
  81.  
  82.         return true;  
  83.     }  
  84.  
  85.     bool Get(Product** ppProduct)  
  86.     {  
  87.         scoped_lock lock(mutex);  
  88.         if (productCount == 0)  
  89.         {  
  90.             {  
  91.                 scoped_lock lock(io_mutex);  
  92.                 std::cout << "Buffer is empty. Waiting..." << std::endl;  
  93.             }  
  94.             while (!stopFlag && (productCount == 0))  
  95.                 cond.wait(lock);  
  96.         }  
  97.         if (stopFlag) // it may be notified by main thread to quit.  
  98.         {  
  99.             *ppProduct = NULL;  
  100.             return false;  
  101.         }  
  102.  
  103.         *ppProduct = pSlot[--productCount];  
  104.         cond.notify_one();  
  105.  
  106.         return true;  
  107.     }  
  108. };  
  109.  
  110. class Producer  
  111. {  
  112. private:  
  113.     Mediator* pMediator;  
  114.     static unsigned int num;  
  115.     unsigned int id;    // Producer id  
  116.  
  117. public:  
  118.     Producer(Mediator* pMediator) : pMediator(pMediator) { id = num++; }  
  119.  
  120.     void operator() ()  
  121.     {  
  122.         Product* pProduct;  
  123. // each thread need to srand differently
  124.         srand( (unsigned)time( NULL ) + id );
  125.         while (!pMediator->Stop())  
  126.         {  
  127.             pProduct = new Product( rand() % 100 );  
  128. // must print product info before call Put, as Put may wake up a consumer  
  129. // and cause *pProuct to be changed  
  130.             {  
  131.                 scoped_lock lock(io_mutex);  
  132.                 std::cout << "Producer[" << id << "] produces Product[" 
  133.                     << *pProduct << "]" << std::endl;  
  134.             }  
  135. // this function only fails when it is notified by main thread to exit  
  136.             if (!pMediator->Put(pProduct))
  137.                 delete pProduct;  
  138.  
  139.             Sleep(100);  
  140.         }  
  141.     }  
  142. };  
  143.  
  144. unsigned int Producer::num = 1;  
  145.  
  146. class Consumer  
  147. {  
  148. private:  
  149.     Mediator* pMediator;  
  150.     static unsigned int num;  
  151.     unsigned int id;    // Consumer id  
  152.  
  153. public:  
  154.     Consumer(Mediator* pMediator) : pMediator(pMediator) { id = num++; }  
  155.  
  156.     void operator() ()  
  157.     {  
  158.         Product* pProduct = NULL;  
  159.         while (!pMediator->Stop())  
  160.         {  
  161.             if (pMediator->Get(&pProduct))  
  162.             {  
  163.                 scoped_lock lock(io_mutex);  
  164.                 std::cout << "Consumer[" << id << "] is consuming Product[" 
  165.                     << *pProduct << "]" << std::endl;  
  166.                 delete pProduct;  
  167.             }  
  168.  
  169.             Sleep(100);  
  170.         }  
  171.     }  
  172. };  
  173.  
  174. unsigned int Consumer::num = 1;  
  175.  
  176. int main()  
  177. {  
  178.     Mediator mediator(2);    // we have only 2 slot to put products  
  179.  
  180.     // we have 2 producers  
  181.     Producer producer1(&mediator);  
  182.     boost::thread thrd1(producer1);  
  183.     Producer producer2(&mediator);  
  184.     boost::thread thrd2(producer2);  
  185.     // and we have 3 consumers  
  186.     Consumer consumer1(&mediator);  
  187.     boost::thread thrd3(consumer1);  
  188.     Consumer consumer2(&mediator);  
  189.     boost::thread thrd4(consumer2);  
  190.     Consumer consumer3(&mediator);  
  191.     boost::thread thrd5(consumer3);  
  192.  
  193.     // wait 1 second  
  194.     Sleep(1000);  
  195.     // and then try to stop all threads  
  196.     mediator.Stop(true);  
  197.     mediator.NotifyAll();  
  198.  
  199.     // wait for all threads to exit  
  200.     thrd1.join();  
  201.     thrd2.join();  
  202.     thrd3.join();  
  203.     thrd4.join();  
  204.     thrd5.join();  
  205.  
  206.     return 0;  

barrier
barrier类的接口定义如下:

 
 
  1. class barrier : private boost::noncopyable   // Exposition only  
  2. {  
  3. public:  
  4.   // construct/copy/destruct  
  5.   barrier(size_t n);  
  6.   ~barrier();  
  7.  
  8.   // waiting  
  9.   bool wait();  
  10. };  


barrier类为我们提供了这样一种控制线程同步的机制:
前n - 1次调用wait函数将被阻塞,直到第n次调用wait函数,而此后第n + 1次到第2n - 1次调用wait也会被阻塞,直到第2n次调用,依次类推。
barrier::wait的实现十分简单:

 
 
  1. barrier::barrier(unsigned int count)  
  2.     : m_threshold(count), m_count(count), m_generation(0)  
  3. {  
  4.     if (count == 0)  
  5.         throw std::invalid_argument("count cannot be zero.");  
  6. }  
  7.  
  8. bool barrier::wait()  
  9. {  
  10. // m_mutex is the base of barrier and is 
  11. // initilized by it's default constructor.
  12.     boost::mutex::scoped_lock lock(m_mutex);
  13. // m_generation will be 0 for call 1~n-1, and 1 for n~2n - 1, and so on...
  14.     unsigned int gen = m_generation;
  15.  
  16.     if (--m_count == 0)  
  17.     {  
  18.         m_generation++;// cause m_generation to be changed in call n/2n/...  
  19.         m_count = m_threshold;// reset count  
  20.         m_cond.notify_all();// wake up all thread waiting here  
  21.         return true;  
  22.     }  
  23.  
  24.     // if m_generation is not changed, lock current thread.
  25.     while (gen == m_generation)
  26.         m_cond.wait(lock);  
  27.     return false;  


因此,说白了也不过是mutex的一个简单应用。
以下是一个使用barrier的例子:

 
 
  1. #include <boost/thread/thread.hpp>  
  2. #include <boost/thread/barrier.hpp>  
  3.  
  4. int i = 0;  
  5. // call barr.wait 3 * n times will release all threads in waiting
  6. boost::barrier barr(3); 
  7. void thread()  
  8. {  
  9.     ++i;  
  10.     barr.wait();  
  11. }  
  12.  
  13. int main()  
  14. {  
  15.     boost::thread thrd1(&thread);  
  16.     boost::thread thrd2(&thread);  
  17.     boost::thread thrd3(&thread);  
  18.  
  19.     thrd1.join();  
  20.     thrd2.join();  
  21.     thrd3.join();  
  22.  
  23.     return 0;  


如果去掉其中thrd3相关的代码,将使得线程1、2一直处于wait状态,进而使得主线程无法退出。

xtime
xtime是boost::thread中用来表示时间的一个辅助类,它是一个仅包含两个成员变量的结构体:

 
 
  1. struct xtime  
  2. {  
  3. //...  
  4.     xtime_sec_t sec;  
  5.     xtime_nsec_t nsec;  
  6. }; 

condition::timed_wait、thread::sleep等涉及超时的函数需要用到xtime。
需要注意的是,xtime表示的不是一个时间间隔,而是一个时间点,因此使用起来很不方便。为了方便使用xtime,boost提供了一些辅助的xtime操作函数,如xtime_get、xtime_cmp等。
以下是一个使用xtime来执行sleep的例子(跟简单的一句Sleep比起来,实在是太复杂了),其中用到了xtime初始化函数xtime_get:

 
 
  1. #include <boost/thread/thread.hpp>  
  2. #include <boost/thread/xtime.hpp>  
  3. #include <iostream>  
  4.  
  5. int main()  
  6. {  
  7.     boost::xtime xt;  
  8.     boost::xtime_get(&xt, boost::TIME_UTC);    // initialize xt with current time  
  9.     xt.sec += 1;    // change xt to next second  
  10.     boost::thread::sleep(xt);    // do sleep  
  11.  
  12.     std::cout << "1 second sleep over." << std::endl;  
  13.  
  14.     return 0;  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值