c++封装线程

原创 2015年07月29日 17:17:07

线程

(1)Linux线程库有两个流行的线程库,分别是LinuxThreads和NPTL,由于LinuxThreads的某些缺点,已经被NPTL取代,它们都是基于1:1模式实现,即1个用户线程被映射为1个内核线程;故每一个用户线程在内核中有唯一的标识

线程标识

使用pthread_t有问题!!!

void* loop(void*)  
{  
  cout  << "Thread run" << endl;  
  return NULL;  
}  
  
int main()  
{  
  pthread_t t1, t2;  
  
  errno = pthread_create(&t1, NULL, loop, NULL);  
  assert(!errno);  
  pthread_join(t1, NULL);  
  
  pthread_create(&t2, NULL, loop, NULL);  
  assert(!errno);  
  pthread_join(t2, NULL);  
  
  cout  << hex << t1 << " " << t2 << endl;  
  
  return 0;  
}  

说明几点:

(1)验证该代码片段可得:上述t1和t2值相同;Pthreads只能保证同一进程中各线程pthread_t在同一时刻不同,不能保证同一线程具有不同的id;

(2)pthread_t在某些应用场合不能作为线程的标识,比如一个线程已经死亡,而此时创建一个新的线程,可能重用了旧的pthread_t,导致本来与旧线程交互的数据,开始又和新的线程错误执行;

(3)解决办法:由于NPTL基于1:1模式(1个用户线程被映射为1个内核线程)实现;故每一个用户线程在内核中也有唯一的标识,使用gettid()可以获得该标识,为了减少系统调用次数,可使用__thread进行缓存该id,这样只有第一次使用该id才会调用gettid()系统调用,但是可移植性会降低;下述代码中__thread缓存tid;syscall(SYS_gettid)为直接使用系统调用号执行系统调用,即直接获取某执行线程的唯一线程ID;

实现代码如下:

namespace CurrentThread  
{  
  
pid_t gettid()  
{  
  return syscall(SYS_gettid);  
}  
  
__thread pid_t t_tid;  
  
static pthread_t tid()  
{  
  if (t_tid == 0)  
    {  
      t_tid = gettid();  
    }  
  
  return t_tid;  
}  
  
}  

  1. thread声明如下:

  1. namespace CurrentThread  
    {  
      
    extern __thread pid_t t_tid;  
    extern pid_t tid();  
      
    }  
      
    //notice: we should make Thread object's life longer than thread, so we shold always use fun: join,  
    class Thread final  
    {  
    public:  
      Thread(const Thread&) = delete;  
      Thread& operator=(const Thread&) = delete;  
      
      typedef std::function<void()> ThreadFunc;  
      
      explicit Thread(const ThreadFunc& func);  
      
      void start();  
      
      bool running() const  
      {  
        return _running;  
      }  
      
      int join()  
      {  
        int err = pthread_join(_threadId, NULL);  
        return err;  
      }  
      
      pid_t tid() const  
      {  
        return _tid;  
      }  
      
    private:  
      void _runInThread();  
      static void* _threadFunc(void*);  
      
      pid_t _tid;  
      pthread_t _threadId;  
      ThreadFunc _func;  
      bool _running;  
    };  

说明几点:

(1)_tid为线程的唯一标识,也就是利用上述CurrentThread和__thread来实现获得的;

(2)线程与Thread对象的生命周期,要注意Thread对象析构和线程终止的顺序关系;当调用thread.join()函数时,可以保证Thread对象在线程终止之后析构;若没有调用thread.join()函数,将会出现thread先析构的情况,此时C++析构会自动deatch线程,因此保证资源不会泄漏,但是线程在使用继续使用Thread相关资源时,执行过程将会出现未定义情况;因此我们应该保证使用thread.join()函数,即Thread的生命周期要长于线程的生命周期;

thread实现如下:

  1. __thread pid_t  CurrentThread::t_tid = 0;  
      
    pid_t CurrentThread::tid()  
    {  
      if (t_tid == 0)  
        t_tid = syscall(SYS_gettid);  
      
      return t_tid;  
    }  
      
    Thread::Thread(const ThreadFunc& func):  
        _func(func),  
        _running(false)  
    {  
      
    }  
      
    void Thread::start()  
    {  
      assert(!_running);  
      _running = true;  
      
      int err = pthread_create(&_threadId, NULL, Thread::_threadFunc, this);  
      if (err != 0)  
        {  
          cout << "pthread_create system error";  
        }  
    }  
      
    void Thread::_runInThread()  
    {  
      assert(_running);  
      try  
        {  
          _tid = CurrentThread::tid();       //impotant, the time we obtain tid;  
      
          cout << "Thread tid[" << _tid << "] is threadFunc";  
      
          if (_func)  
            _func();  
        }  
      catch (...)  
        {  
          cout << "Exception happen in Thread";  
        }  
    }  
      
    void* Thread::_threadFunc(void* obj)      //remember obj's scope in self class  
    {  
      Thread* thread = static_cast<Thread*>(obj);  
      thread->_runInThread();  
      return NULL;  
    }  

说明几点:

(1)static void* _threadFunc(void*)为Thread的static成员函数,而不是成员函数,因为pthread_create(&_threadId, NULL, Thread::_threadFunc, this)接收的函数类型必须是一个类似全局的函数,使用非static成员函数,显然我们还要传递相对应的对象指针,pthread_create是无法接收的;
(2)在_runInThread()中,使用 catch (...)捕捉线程执行过程中的一切异常;
测试代码:
  1. #include <Base/Thread.h>  
    #include <stdio.h>  
      
    using namespace std;  
    using namespace Base;  
      
    void func1() {  
      printf("%s %d\n", "func1: tid", CurrentThread::tid());  
    }  
      
    void func2() {  
       printf("%s %d\n", "func2: tid", CurrentThread::tid());  
    }  
      
    int main(void) {  
      Thread thread1(func1);  
      Thread thread2(func2);  
        
      thread1.start();  
      thread2.start();  
        
      thread1.join();  
      thread2.join();  
      return 0;  
    }  





版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

C++对windows/linux线程的简单封装

  • 2014年04月15日 23:00
  • 838B
  • 下载

(转)Linux平台用C++封装线程读写锁

转载自:http://blog.csdn.net/chexlong/article/details/7163233       在Linux平台上已经有现成的线程读写锁pthread_rwlock...
  • yuxq100
  • yuxq100
  • 2011年12月31日 09:07
  • 293

C++封装一个简单的线程类

多线程编程简介:     大家在编程时,经常需要在程序中启动一个或多个线程来处理任务,而如果每次都是去调用系统创建线程的API函数来创建,代码量虽不多,但线程的创建和业务逻辑代码就揉在一起了,且...
  • a675311
  • a675311
  • 2015年10月19日 15:47
  • 192

C++实现的可以安全的暂停、继续、停止线程的封装类

在使用线程时,最麻烦的就是线程的同步控制,如暂停、继续、停止(包括暂停状态下)等。虽然微软提供了 SuspendThread、TerminateThread 等函数“似乎”可以完成这个功能,但如果你在...
  • fishjam
  • fishjam
  • 2012年04月04日 16:15
  • 4911

c++ 线程并发、任务队列、异步 任务封装和分发 lambda与任务 boost

在开发C++程序时,一般在吞吐量、并发、实时性上有较高的要求。设计C++程序时,总结起来可以从如下几点提高效率: 并发异步缓存 下面将我平常工作中遇到一些问题例举一二,其设计思想无非以上三...

C++封装一个简单的线程类

多线程编程简介:     大家在编程时,经常需要在程序中启动一个或多个线程来处理任务,而如果每次都是去调用系统创建线程的API函数来创建,代码量虽不多,但线程的创建和业务逻辑代码就揉在一起了,且...
  • zs10hz
  • zs10hz
  • 2014年08月05日 17:57
  • 159

Linux平台用C++封装线程读写锁

在Linux平台上已经有现成的线程读写锁pthread_rwlock_t以及相关API,现将这些API封装成与Win32平台上相同的接口,以便于编写跨平台程序。这些API包括pthread_rwloc...

用C++封装Win32信号量,同步线程

在Win32环境下编写多线程应用程序,也会常用到信号量Semaphore来进行线程同步。与其相关的一组API包括:CreateSemaphore,ReleaseSemaphore,WaitForSin...

linux C++ 面向对象线程类封装

1.封装遇到的问题 将pthread线程封装为抽象类,这样用户在使用线程时,只需要继承一下这个抽象类,并实现相应的接口就可以了。这样做的好处是 用户可以将注意力集中在线程所要执行的逻辑上,而不需要关注...

C++封装POSIX 线程库(六)线程池

线程池有两个主要组件: 1.threads 2.blocking queue 解决的问题是线程创建与销毁带来的开销和通过线程池实现伪异步 过程类似于简单的生产者消费者问题(详见wiki)B...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:c++封装线程
举报原因:
原因补充:

(最多只允许输入30个字)