网络编程入门05
这几天因为工作需求,学习了多线程编程,用线程池写了一个小工具,对于网络库的回调,多线程理解加深了一层理解。发觉,利用理解多线程,理解这些小的组件,也非常重要。被工作打断,直接阅读net模块有点难度,打算从base模块阅读起。
class noncopyable{} //把赋值构造和赋值操作直接删除,默认构造和析构使用默认操作。
class copyable{} //把构造和析构设置默认,其余不变,编译器设置
//编译器本身提供的原子操作
type __sync_fetch_and_add (type *ptr, type value, ...)
type __sync_fetch_and_sub (type *ptr, type value, ...)
type __sync_fetch_and_or (type *ptr, type value, ...)
type __sync_fetch_and_and (type *ptr, type value, ...)
type __sync_fetch_and_xor (type *ptr, type value, ...)
type __sync_fetch_and_nand (type *ptr, type value, ...)
type __sync_add_and_fetch (type *ptr, type value, ...)
type __sync_sub_and_fetch (type *ptr, type value, ...)
type __sync_or_and_fetch (type *ptr, type value, ...)
type __sync_and_and_fetch (type *ptr, type value, ...)
type __sync_xor_and_fetch (type *ptr, type value, ...)
type __sync_nand_and_fetch (type *ptr, type value, ...)
//这两组函数的区别在于第一组返回更新前的值,第二组返回更新后的值。
bool __sync_bool_compare_and_swap (type *ptr, type oldval type newval, ...)
type __sync_val_compare_and_swap (type *ptr, type oldval type newval, ...)
这两个函数提供原子的比较和交换,如果*ptr == oldval,就将newval写入*ptr,
第一个函数在相等并写入的情况下返回true.
第二个函数在返回操作之前的值。
type __sync_lock_test_and_set (type *ptr, type value, ...)
将*ptr设为value并返回*ptr操作之前的值。
void __sync_lock_release (type *ptr, ...)
将*ptr置0
AtomicIntegerT 原子类的实现利用了gcc提供原子操作,在c++11中有了std::atomic<int> 类
template <typename T>
class AtomicIntegerT:noncopyable{
public:
T get() {
return __sync_val_compare_and_swap(&value_, 0, 0);
}
T getAndAdd(T x) {
return __sync_fetch_and_add(&value_, x);
}
T addAndGet(T x) {
return getAndAdd(x) + x;
}
T incrementAndGet() {
return addAndGet(1);
}
T decrementAndGet() {
return addAndGet(-1);
}
void add(T x) {
getAndAdd(x);
}
void increment() {
getAndAdd(1);
}
void decrement() {
getAndAdd(-1);
}
T getAndSet(T x) {
return __sync_lock_test_and_set(&value_, x);
}
private:
volatile T value_; 初始化为0
}
typedef detail::AtomicIntegerT<int32_t> AtomicInt32;
typedef detail::AtomicIntegerT<int64_t> AtomicInt64;
关于Type.h文件 主要是对c++基本转换做了一个封装
void memZero(void* p, size_t n), 从p开始的n个字节清空操作
template<typename To, typename From>
inline To implicit_cast(From const &f) 一个模板方法,从from到to类型的隐式转换
{
return f;
}
template<typename To, typename From> // use like this: down_cast<T*>(foo); 从基类指针向子类指针转换
inline To down_cast(From* f) // so we only accept pointers
{
// Ensures that To is a sub-type of From *. This test is here only
// for compile-time type checking, and has no overhead in an
// optimized build at run-time, as it will be optimized away
// completely.
if (false)
{
implicit_cast<From*, To>(0);
}
#if !defined(NDEBUG) && !defined(GOOGLE_PROTOBUF_NO_RTTI)
assert(f == NULL || dynamic_cast<To>(f) != NULL); // RTTI: debug mode only!
#endif
return static_cast<To>(f);
}
CurrentThread.h 属于一个命名空间
//__thread修饰的全局变量每个线程各有一份实体,互不影响。初始化值只能是编译期常量。
extern __thread int t_cachedTid;
extern __thread char t_tidString[32];
extern __thread int t_tidStringLength;
extern __thread const char* t_threadName;
__builtin_expect
这个指令是gcc引入的,作用是允许程序员将最有可能执行的分支告诉编译器。这个指令的写法为:__builtin_expect(EXP, N)。
意思是:EXP==N的概率很大。
一般的使用方法是将__builtin_expect指令封装为likely和unlikely宏。这两个宏的写法如下.
#define likely(x) __builtin_expect(!!(x), 1) //x很可能为真
#define unlikely(x) __builtin_expect(!!(x), 0) //x很可能为假
//内核中的 likely() 与 unlikely()
if(likely(value)) //等价于 if(value)
if(unlikely(value)) //也等价于 if(value)
void cacheTid();
int tid(){
if (__builtin_expect(t_cachedTid == 0, 0))
{
cacheTid(); //这个函数在Thread.cc中进行实现
}
return t_cachedTid;
}
在Thread.cc
void CurrentThread::cacheTid()
{
if (t_cachedTid == 0)
{
t_cachedTid = detail::gettid();
t_tidStringLength = snprintf(t_tidString, sizeof t_tidString, "%5d ", t_cachedTid);
}
}
在Thread.cc 的detail命名空间下
//syscall(SYS_gettid))
>NAME
>syscall - 间接系统调用
// syscall() 执行一个系统调用,根据指定的参数number和所有系统调用的汇编语言接口来确定调用哪个系统调用。
// 系统调用所使用的符号常量可以在头文件里面找到。
pid_t gettid()
{
return static_cast<pid_t>(::syscall(SYS_gettid));
}
const char* tidString() {return t_tidString;}
int tidStringLength() { return t_tidStringLength; }
const char* name() { return t_threadName;}
bool isMainThread(); //在thread.cc中实现
{
return tid() == ::getpid();
}
struct timespec;
typedef long time_t;
#ifndef _TIMESPEC
#define _TIMESPEC
struct timespec {
time_t tv_sec; // seconds
long tv_nsec; // and nanoseconds
};
nanosleep( )---------以纳秒为单位
#include<time.h>
struct timespec
{
time_t tv_sec; /* 秒seconds */
long tv_nsec; /* 纳秒nanoseconds */
};
int nanosleep(const struct timespec *req, struct timespec *rem);
//return: 若进程暂停到参数*req所指定的时间,成功则返回0,若有信号中断则返回-1,并且将剩余微秒数记录在*rem中。
//req->tv_sec是以秒为单位,而tv_nsec以毫微秒为单位(10的-9次方秒)。
//由于调用nanosleep是是进程进入TASK_INTERRUPTIBLE,这种状态是会相应信号而进入TASK_RUNNING状态的。
void sleepUsec(int64_t usec); //在thread.cc实现
{
struct timespec ts = { 0, 0 };
ts.tv_sec = static_cast<time_t>(usec / Timestamp::kMicroSecondsPerSecond);
ts.tv_nsec = static_cast<long>(usec % Timestamp::kMicroSecondsPerSecond * 1000);
::nanosleep(&ts, NULL);
}
#include <execinfo.h>
/* Store up to SIZE return address of the current program state in
ARRAY and return the exact number of values stored. */
int backtrace(void **array, int size);
/* Return names of functions from the backtrace list in ARRAY in a newly
malloc()ed memory block. */
char **backtrace_symbols(void *const *array, int size);
/* This function is similar to backtrace_symbols() but it writes the result
immediately to a file. */
void backtrace_symbols_fd(void *const *array, int size, int fd);
string stackTrace(bool demangle); //打印信息栈操作,普通程序员不用。在CurrentThread.cc中实现
Mutex.h 是对mutex的封装
GNU C 的一大特色就是__attribute__ 机制。attribute 可以设置函数属性(Function Attribute )、变量属性(Variable Attribute )和类型属性(Type Attribute )。
attribute 书写特征是:attribute 前后都有两个下划线,并切后面会紧跟一对原括弧,括弧里面是相应的__attribute__ 参数。
attribute 语法格式为:attribute ((attribute-list))
class MutexLock : noncopyable{
private:
friend class Condition;//右元类
pthread_mutex_t mutex_;
pid_t holder_;
class UnassignGuard : noncopyable{ //属于MutexLock的一个内部类,含有一个MutexLock的引用
public:
ctr(MutexLock& owner) : owner_(owner) {
owner_.unassignHolder();
}
~dtr() {
owner_.assignHolder();
}
private:
MutexLock& owner_;
}
}
class MutexLockGuard : noncopyable{
private:
MutexLock& mutex_;
public:
MutexLockGuard(MutexLock& mutex) : mutex_(mutex) {
mutex_.lock();
}
~MutexLockGuard() {
mutex_.unlock();
}
}
Condition.h 对应于C++11的条件变量封装,基本上使用pthread库元素mutex和pthread_cond_t原生变量实现c++11的条件变量
/* 初始化一个条件变量 */
int pthread_cond_init (pthread_cond_t* cond, pthread_condattr_t *cond_attr);
/* 销毁一个条件变量 */
int pthread_cond_destroy(pthread_cond_t* cond);
/* 令一个消费者等待在条件变量上 */
int pthread_cond_wait(pthread_cond_t* cond);
/* 生产者通知等待在条件变量上的消费者 */
int pthread_cond_signal(pthread_cond_t* cond);
/* 生产者向消费者广播消息 */
int pthread_cond_broadcast(pthread_cond_t* cond);
class Condition : noncopyable {
private:
MutexLock& mutex_;
pthread_cond_t pcond_;
public:
Condition(MutexLock& mutex) : mutex_(mutex) {
pthread_cond_init(&pcond_, NULL);
}
~Condition() {
pthread_cond_destory(&pcond_);
}
void wait() {
MutexLock::UnassignGuard ug(mutex_); //利用栈上对象,实现对锁的释放,在结束,对锁资源回收
pthread_cond_wait(&pcond_, mutex_.getPthreadMutex());
}
void notify() {
pthread_cond_signal(&pcond_); //通知一个等待此条件变量的线程
}
bool waitForSeconds(double seconds);// 等待一定时间
void notifyAll() {
pthread_cond_broadcast(&pcond_); //通知所有线程
}
}
CountDownLatch.h 一个倒计时的condition_variable; 当count数字减为0,一次通知,所有倒数钱的wait都会进入等待队列
class CountDownLatch : noncopyable {
private:
mutable MutexLock mutex_;
Condition condition_;
int count_;
public:
CountDownLatch(int count):mutex_(), condition_(mutex_), count_(count) {
}
void wait() {
MutexLockGuard lock(mutex_);
while (count_ > 0) {
condition_.wait();
}
}
void countDown() {
MutexLockGuard lock(mutex_);
--count_;
if (count_ == 0) {
condition_.notifyAll();
}
}
int getCount() const {
MutexLockGuard lock(mutex_);
return count_;
}
}
Thread.h里有些关于线程创建,运行的封装
函数简介
pthread_create是UNIX环境创建线程函数
头文件
#include<pthread.h>
函数声明
int pthread_create(pthread_t restrict tidp,const pthread_attr_t restrict_attr,void(start_rtn)(void*),void *restrict arg);
返回值
若成功则返回0,否则返回出错编号
参数
第一个参数为指向线程标识符的指针。
第二个参数用来设置线程属性。
第三个参数是线程运行函数的地址。
最后一个参数是运行函数的参数。
void* startThread(void* obj) {
ThreadData* data = static_cast<ThreadData*> (obj);
data->runInThread();
delete data;
return NULL;
}
class Thread : noncopyable {
private:
void setDefaultName() {
int num = numCreated_.incrementAndGet();
if (name_.empty())
{
char buf[32];
snprintf(buf, sizeof buf, "Thread%d", num);
name_ = buf;
}
}
bool started_;
bool joined_;
pthread_t pthreadId_;
pid_t tid_;
ThreadFunc func_;
string name;
CountDownLatch latch_;
static AtomicInt32 numCreated_;
public:
typedef std::function<void()> ThreadFun; //对一个没有返回值的可调用对象定义为线程函数,主要为线程需要执行的任务
Thread(ThreadFunc, const string&name = string()) //构造函数,传入一个线程函数,和名字
:started_(false),
joined_(false),
pthreadId_(0),
tid_(0),
func_(std::move(func)),
name_(n),
latch_(1) {
setDefaultName();
}
~Thread{
if (started_ && !joined_) {
pthread_detach(pthreadId);
}
}
void start() {
assert(!started_);
started_ = true;
ThreadData* data = new ThreadData(func_, name_, &tid, &latch_);
if (pthread_create(&pthreadId_, NULL, &startThread, data)) { //返回非0值,认为失败
started_ = false;
delete data;
}
else {
latch_.wait(); //主线程等待条件变量
assert(tid_ > 0);
}
}
int join() {
assert(started_);
assert(!joined_);
joined_ = true;
return pthread_join(pthreadId_, NULL);
}
bool started() {
return started_;
}
pid_t tid() {
return tid_;
}
const string& name const {
return name_;
}
static int numCreated() {
return numCreated_.get();
}
}
struct ThreadData{ //data里拿的几乎都是指针
typedef muduo::Thread::ThreadFunc ThreadFunc; //简化命名
ThreadFunc func_;
string name_;
pid_t* tid_;
CountDownLatch* latch_;
ThreadData(ThreadFunc func,
const string& name,
pid_t* tid,
CountDownLatch* latch)
: func_(std::move(func)),
name_(name),
tid_(tid),
latch_(latch)
{ }
void runInThread() //基本上就是把ThreadFunc 跑起来,加上了捕获异常安全检查
{
*tid_ = muduo::CurrentThread::tid();
tid_ = NULL;
latch_->countDown();
latch_ = NULL;
muduo::CurrentThread::t_threadName = name_.empty() ? "muduoThread" : name_.c_str();
::prctl(PR_SET_NAME, muduo::CurrentThread::t_threadName); //这是一个系统调用,几乎用不到的东西
try
{
func_();
muduo::CurrentThread::t_threadName = "finished";
}
catch (const Exception& ex)
{
muduo::CurrentThread::t_threadName = "crashed";
fprintf(stderr, "exception caught in Thread %s\n", name_.c_str());
fprintf(stderr, "reason: %s\n", ex.what());
fprintf(stderr, "stack trace: %s\n", ex.stackTrace());
abort();
}
catch (const std::exception& ex)
{
muduo::CurrentThread::t_threadName = "crashed";
fprintf(stderr, "exception caught in Thread %s\n", name_.c_str());
fprintf(stderr, "reason: %s\n", ex.what());
abort();
}
catch (...)
{
muduo::CurrentThread::t_threadName = "crashed";
fprintf(stderr, "unknown exception caught in Thread %s\n", name_.c_str());
throw; // rethrow
}
}
void afterFork()
{
muduo::CurrentThread::t_cachedTid = 0;
muduo::CurrentThread::t_threadName = "main";
CurrentThread::tid();
// no need to call pthread_atfork(NULL, NULL, &afterFork);
}
class ThreadNameInitializer
{
public:
ThreadNameInitializer()
{
muduo::CurrentThread::t_threadName = "main";
CurrentThread::tid();
pthread_atfork(NULL, NULL, &afterFork);
}
};
ThreadNameInitializer init;
ThreadLocal.h 文件对线程存储的一个包装
线程中特有的线程存储, Thread Specific Data 。线程存储有什么用了?他是什么意思了?大家都知道,在多线程程序中,所有线程共享程序中的变量。现在有一全局变量,所有线>程都可以使用它,改变它的值。而如果每个线程希望能单独拥有它,那么就需要使用线程存储了。表面上看起来这是一个全局变量,所有线程都可以使用它,而它的值在每一个线程>中又是单独存储的。这就是线程存储的意义
创建一个类型为pthread_key_t类型的变量。
调用pthread_key_create()来创建该变量。该函数有两个参数,第一个参数就是上面声明的pthread_key_t变量,第二个参数是一个清理函数,用来在线程释放该线程存储的时候被调>用。该函数指针可以设成 NULL,这样系统将调用默认的清理函数。该函数成功返回0.其他任何返回值都表示出现了错误。
当线程中需要存储特殊值的时候,可以调用 pthread_setspcific() 。该函数有两个参数,第一个为前面声明的pthread_key_t变量,第二个为void*变量,这样你可以存储任何类型的>?>值。
如果需要取出所存储的值,调用pthread_getspecific()。该函数的参数为前面提到的pthread_key_t变量,该函数返回void *类型的值。下面是前面提到的函数的原型:
int pthread_setspecific(pthread_key_t key, const void *value);
void *pthread_getspecific(pthread_key_t key);
int pthread_key_create(pthread_key_t *key, void (*destructor)(void*));
template<typename T>
class ThreadLoacl : noncopable {
private:
pthread_key_t pkey_;
static void destrutor(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;
ThreadLocal() {
pthread_key_create(&pkey_, &ThreadLoacl::destructor);
}
~ThreadLocal() {
pthread_key_delete(pkey_);
}
T& value() {
T* perThreadValue = static_cast<T*> (pthread_getspecific(pkey_));
if (!perThreadValue) {
T* newObj = new T();
pthread_setspecific(pkey_, newObj);
perThreadValue = newobj;
}
return *perThreadValue;
}
}
Singleton.h 对单例模式的实现 模板里套模板。。。需要对模板编程有点了解
detail::
template<typename T>
struct has_no_destory{
template<typename C>
static char test(decltype(&C::no_destory));
template <typename C> static int32_t test(...);
const static bool value = sizeof(test<T>(0)) == 1;
}
muduo::
template<typename T>
class Singleton : noncopyable {
private:
static pthread_once_t ponce_;
static T* value_;
public:
Singleton() = delete;
~Singleton() = delete;
static T& instance() {
pthread_once(&ponce_, &Singleton::init);
assert(value_ != NULL);
return *value_;
}
private:
static void init()
{
value_ = new T();
if (!detail::has_no_destroy<T>::value)
{
::atexit(destroy);
}
}
static void destroy()
{
typedef char T_must_be_complete_type[sizeof(T) == 0 ? -1 : 1];
T_must_be_complete_type dummy; (void) dummy;
delete value_;
value_ = NULL;
}
}
template<typename T>
pthread_once_t Singleton<T>::ponce_ = PTHREAD_ONCE_INIT;
template<typename T>
T* Singleton<T>::value_ = NULL;
ThreadLocalSingleton.h ThreadLocal单例模式
template<typename T>
class ThreadLocalSingleton : noncopyable
{
public:
ThreadLocalSingleton() = delete;
~ThreadLocalSingleton() = delete;
static T& instance()
{
if (!t_value_)
{
t_value_ = new T();
deleter_.set(t_value_);
}
return *t_value_;
}
static T* pointer()
{
return t_value_;
}
private:
static void destructor(void* obj)
{
assert(obj == t_value_);
typedef char T_must_be_complete_type[sizeof(T) == 0 ? -1 : 1]; ///来将运行时错误转变为编译期错误。这句话其实就是定义了一个固定大小的char型数组,数组名为
//type_must_be_complete,数组大小是多少呢?是sizeof(T)?1:-1, ?:
//这个三元操作符大家都很熟悉了,若sizeof(T)非0,这个表达式的值为1,
//即typedef了一个大小为1的char型数组,否则定义一个大小为-1的数组。数组大小还能为负数?
//当然不能,于是就会报错,而且是编译期错误,于是就将一个动态运行时错误在编译时就发现了。
T_must_be_complete_type dummy; (void) dummy;
delete t_value_;
t_value_ = 0;
}
class Deleter
{
public:
Deleter()
{
pthread_key_create(&pkey_, &ThreadLocalSingleton::destructor);
}
~Deleter()
{
pthread_key_delete(pkey_);
}
void set(T* newObj)
{
assert(pthread_getspecific(pkey_) == NULL);
pthread_setspecific(pkey_, newObj);
}
pthread_key_t pkey_;
};
static __thread T* t_value_;
static Deleter deleter_;
};
template<typename T>
__thread T* ThreadLocalSingleton<T>::t_value_ = 0;
template<typename T>
typename ThreadLocalSingleton<T>::Deleter ThreadLocalSingleton<T>::deleter_;
ThreadPool.h 线程池的设计
class ThreadPool : noncopyable
{
private:
private:
bool isFull() const REQUIRES(mutex_); //判断队列是否满
void runInThread(); //线程循环
Task take(); //从任务队列取一个任务
mutable MutexLock mutex_; //锁
Condition notEmpty_ GUARDED_BY(mutex_); //非空条件变量
Condition notFull_ GUARDED_BY(mutex_); //非满条件变量
string name_;
Task threadInitCallback_; //处理任务的对象
std::vector<std::unique_ptr<muduo::Thread>> threads_; //线程队列
std::deque<Task> queue_ GUARDED_BY(mutex_); //任务队列
size_t maxQueueSize_; //任务队列大小
bool running_; //判断是否还在运行
public:
typedef std::function<void ()> Task; //一个可调用对象包装成任务
explicit ThreadPool(const string& nameArg = string("ThreadPool")) //初始化
: mutex_(),
notEmpty_(mutex_),
notFull_(mutex_),
name_(nameArg),
maxQueueSize_(0),
running_(false)
{}
~ThreadPool() {
if (running_) {
stop();
}
}
// Must be called before start().
void setMaxQueueSize(int maxSize) { maxQueueSize_ = maxSize; }
void setThreadInitCallback(const Task& cb)
{ threadInitCallback_ = cb; }
void start(int numThreads) {
assert(threads_.empty()); //确定线程池为空
running_ = true;
threads_.reserve(numThreads);
for (int i = 0; i < numThreads; ++i)
{
char id[32];
snprintf(id, sizeof id, "%d", i+1);
threads_.emplace_back(new muduo::Thread(
std::bind(&ThreadPool::runInThread, this), name_+id)); //每一个线程的启动函数都是runInThread
threads_[i]->start();
}
if (numThreads == 0 && threadInitCallback_)
{
threadInitCallback_();
}
}
void stop(){
{
MutexLockGuard lock(mutex_);
running_ = false;
notEmpty_.notifyAll();
}
for (auto& thr : threads_)
{
thr->join();
}
}
const string& name() const
{ return name_; }
size_t queueSize() const {
MutexLockGuard lock(mutex_);
return queue_.size();
}
void run(Task f) {
if (threads_.empty()) //如果没有子线程,就在ThreadPool当前线程执行
{
task();
}
else //把任务加入任务队列
{
MutexLockGuard lock(mutex_);
while (isFull())
{
notFull_.wait();
}
assert(!isFull());
queue_.push_back(std::move(task));
notEmpty_.notify();
}
Task take() { //从任务队列中去一个任务
MutexLockGuard lock(mutex_);
// always use a while-loop, due to spurious wakeup
while (queue_.empty() && running_)
{
notEmpty_.wait();
}
Task task;
if (!queue_.empty())
{
task = queue_.front();
queue_.pop_front();
if (maxQueueSize_ > 0)
{
notFull_.notify();
}
}
return task;
}
}
void runInThread() {
if (threadInitCallback_)
{
threadInitCallback_();
}
while (running_) //取出任务,执行. 加入了一些异常处理
{
Task task(take());
if (task)
{
task();
}
}
}