muduo_base代码剖析之线程特定数据的封装ThreadLocal

版权声明:guojawee https://blog.csdn.net/weixin_36750623/article/details/84325778

线程特定数据 (线程内私有的全局变量)来源

  1. 在单线程程序中,我们经常要用到“全局变量”以实现多个函数间能共享数据
  2. 在多线程环境下,由于数据空间是共享的,因此全局变量也为所有线程共享
  3. 但有时应用程序设计中有必要提供线程私有的全局变量,仅在某个线程中生效,但却可以跨多个函数访问
  4. POSIX线程库通过维护一定的数据结构来解决这个问题,这些数据称为Thread-specific Data / TSD
  5. 线程特定数据也称为本地存储TLS
  6. 对于POD类型的线程本地存储,可以使用__thread关键字

POSIX线程特定数据

在这里插入图片描述

  1. 一旦有任意一个线程调用了pthread_key_create创建了key,那么所有的线程都拥有自己的key
  2. 可以为特定线程,设置特定数据,这样不同的线程就会有不同的特定数据(虽然它们的key相同,但是key对应的实际数据不同)
  3. 怎么销毁线程的特定数据呢?答:在pthread_key_create函数中指定key的销毁的回调函数

muduo实现的线程特定数据ThreadLocal<T>

#ifndef MUDUO_BASE_THREADLOCAL_H
#define MUDUO_BASE_THREADLOCAL_H

#include <muduo/base/Mutex.h>  // MCHECK
#include <muduo/base/noncopyable.h>

#include <pthread.h>

namespace muduo
{

template<typename T>
class ThreadLocal : noncopyable
{
private:
  pthread_key_t pkey_;

public:
  ThreadLocal()
  {
	//创建线程特定数据pkey_,并指定销毁实际数据的回调函数destructor
    MCHECK(pthread_key_create(&pkey_, &ThreadLocal::destructor));
  }

  ~ThreadLocal()
  {
    MCHECK(pthread_key_delete(pkey_));
  }

  T& value()
  {
	//pthread_getspecific:通过pkey_获取线程特定数据对应的实际数据
    T* perThreadValue = static_cast<T*>(pthread_getspecific(pkey_));
    if (!perThreadValue) //返回值为空,说明pkey_对应的实际数据还没创建
    {
      T* newObj = new T(); //就创建pkey_对应的实际数据
      MCHECK(pthread_setspecific(pkey_, newObj)); //为pkey_设置对应的实际数据
      perThreadValue = newObj;
    }
    return *perThreadValue;
  }
  
private:
  static void destructor(void *x)
  {
    T* obj = static_cast<T*>(x);
    typedef char T_must_be_complete_type[sizeof(T) == 0 ? -1 : 1]; //检查是否是完全类型
    T_must_be_complete_type dummy; (void) dummy;
    delete obj;
  }
};

测试程序与使用特定数据的方法

使用特定数据的方法

  1. 将自定义类Test,作为模板参数T传给ThreadLocal,即muduo::ThreadLocal<Test>
  2. 定义全局的特定数据,即muduo::ThreadLocal<Test> testObj;
    此时,所有的线程都拥有变量testObj,只是每个线程中testObj对应的实际数据是不同的
  3. 在任意一个线程中,可以使用testObj.value()函数获得特定类型对应的实际的Test*,进而使用Test*去访问Test中的数据

示例代码1

#include <muduo/base/ThreadLocal.h>
#include <muduo/base/CurrentThread.h>
#include <muduo/base/Thread.h>

#include <stdio.h>

class Test : muduo::noncopyable
{
 public:
  Test()
  {
    printf("tid=%d, constructing %p\n", muduo::CurrentThread::tid(), this);
  }

  ~Test()
  {
    printf("tid=%d, destructing %p %s\n", muduo::CurrentThread::tid(), this, name_.c_str());
  }

  const muduo::string& name() const { return name_; }
  void setName(const muduo::string& n) { name_ = n; }

 private:
  muduo::string name_;
};

//定义线程特定函数,表示每个线程都有自己独立的特定数据
muduo::ThreadLocal<Test> testObj1;
muduo::ThreadLocal<Test> testObj2;

void print()
{
  printf("tid=%d, obj1 %p name=%s\n",
         muduo::CurrentThread::tid(),
         &testObj1.value(),
         testObj1.value().name().c_str());
  printf("tid=%d, obj2 %p name=%s\n",
         muduo::CurrentThread::tid(),
         &testObj2.value(),
         testObj2.value().name().c_str());
}

void threadFunc()
{
  print();
  testObj1.value().setName("changed 1");
  testObj2.value().setName("changed 42");
  print();
}

int main()
{
  //主线程
  testObj1.value().setName("main one");
  print();
  
  //线程1
  muduo::Thread t1(threadFunc);
  t1.start();
  t1.join();
  
  //主线程
  testObj2.value().setName("main two");
  print();

  pthread_exit(0);
}

示例代码2

/*
该示例表达的用意是:

  1. 所有的线程访问的都是muduo::Singleton<muduo::ThreadLocal<Test> >::instance()对象,是相同的
  2. 但是每个线程中muduo::Singleton<muduo::ThreadLocal<Test> >::instance()对象对应的特定数据value()是线程私有的,是不同的
    */

下面代码总结为一句话:所有的线程公用一个实例instance(),但是各个线程拥有自己私有的特定数据instance().value()

#include <muduo/base/Singleton.h>
#include <muduo/base/CurrentThread.h>
#include <muduo/base/ThreadLocal.h>
#include <muduo/base/Thread.h>

#include <stdio.h>
#include <unistd.h>

class Test : muduo::noncopyable
{
 public:
  Test()
  {
    printf("tid=%d, constructing %p\n", muduo::CurrentThread::tid(), this);
  }

  ~Test()
  {
    printf("tid=%d, destructing %p %s\n", muduo::CurrentThread::tid(), this, name_.c_str());
  }

  const muduo::string& name() const { return name_; }
  void setName(const muduo::string& n) { name_ = n; }

 private:
  muduo::string name_;
};
/*
该示例表达的用意是:
  1. 所有的线程访问的都是muduo::Singleton<muduo::ThreadLocal<Test> >::instance()对象 
  2. 但是每个线程中muduo::Singleton<muduo::ThreadLocal<Test> >::instance()对象对应的
     特定数据value()是线程私有的,是不同的
*/
#define STL muduo::Singleton<muduo::ThreadLocal<Test> >::instance().value()

void print()
{
  printf("tid=%d, %p name=%s\n",
         muduo::CurrentThread::tid(),
         &STL,
         STL.name().c_str());
}

void threadFunc(const char* changeTo)
{
  print();
  STL.setName(changeTo);
  sleep(1);
  print();
}

int main()
{
  STL.setName("main one");
  muduo::Thread t1(std::bind(threadFunc, "thread1"));
  muduo::Thread t2(std::bind(threadFunc, "thread2"));
  t1.start();
  t2.start();
  t1.join();
  print();
  t2.join();
  pthread_exit(0);
}

猜你喜欢

转载自blog.csdn.net/weixin_36750623/article/details/84325778
今日推荐