[学习分享]----sylar服务器框架源码阅读--线程模块


sylar作者在本站的地址为 这里,也可以查看 作者主页,也有视频教程可以 点击这里

一、 进程与线程

  1. 进程: 在操作系统中, 每个运行的应用程序就是一个进程。每个进程都有自己的内存空间,不同进程间的内存空间是相互隔离的,每个进程信息都保存在操作系统的内核空间中。
  2. 线程:就是进程中的执行体,他需要指定的执行入口,通常为某个函数的指令入口。一个进程至少有一个线程,他要从这个线程开始执行,一般被称为进程的主线程,可以认为主线程是进程的第一个线程,一般是由父进程或者操作系统直接创建的。一个进程也可以有多个线程,而这些额外的线程都是由主线程创建的,这些线程可以共享进程的地址空间和句柄表等资源。既然可以共享进程中的资源,就免不了线程安全方面的的设计。

二、Sylar源码分析

namespace sylar {

//线程类
class Thread : Noncopyable {
public:
	// 线程智能指针类型
    typedef std::shared_ptr<Thread> ptr;
	// 线程构造函数
	// cb 线程执行函数
	// name 线程名称
    Thread(std::function<void()> cb, const std::string& name);

    ~Thread();

    // 获取线程Id
    pid_t getId() const { return m_id;}
	// 获取线程姓名
    const std::string& getName() const { return m_name;}
	
    // 等待线程执行完成
    void join();

    // 得到当前正在运行的线程指针
    static Thread* GetThis();

    // 得到当前线程名称
    static const std::string& GetName();

    // 设置当前线程名称
    static void SetName(const std::string& name);

private:
    static void* run(void* arg);
private:
    // 线程Id
    pid_t m_id = -1;
	// 线程结构句柄
    pthread_t m_thread = 0;
	// 线程执行函数体
    std::function<void()> m_cb;
	// 线程名称
    std::string m_name;
	// 信号量
    Semaphore m_semaphore;
};
}

以下一小段是sylar作者原话
线程模块,封装了pthread里面的一些常用功能。 Thread,Semaphore,Mutex,RWMutex,Spinlock等对象,可以方便开发中对线程日常使用
为什么不适用c++11里面的thread
本框架是使用C++11开发,不使用thread,是因为thread其实也是基于pthread实现的。并且C++11里面没有提供读写互斥量,RWMutex,Spinlock等,在高并发场景,这些对象是经常需要用到的。所以选择了自己 封装pthread

接下来介绍一下线程模块使用的信号量的封装

class Semaphore : Noncopyable {
public:
    /**
     * @brief 构造函数
     * @param[in] count 信号量值的大小
     */
    Semaphore(uint32_t count = 0);

    /**
     * @brief 析构函数
     */
    ~Semaphore();

    /**
     * @brief 获取信号量
     */
    void wait();

    /**
     * @brief 释放信号量
     */
    void notify();
private:
    sem_t m_semaphore;
};

// 以下是函数实现
Semaphore::Semaphore(uint32_t count) {
    if(sem_init(&m_semaphore, 0, count)) {
        throw std::logic_error("sem_init error");
    }
}

/*  throw用法简单介绍
	C++通过 “throw” 关键字 抛出一条表达式来触发一个异常
	它通常作为 条件语句的一部分 或者 作为某个函数的最后一条语句,
	当throw执行时,跟在throw后面的语句将不再被执行,
	程序的控制权从throw转移到与之匹配的catch
	(catch可能是同一个函数中的局部catch,也可能位于调用链上的其他函数)。
	try {
	throw expression;
	}
	catch(case 1) {
	
	}
	catch(case 2) {
	
	}
	catch(case 3) {
	
	}
	所谓 “try”,就是 “尝试着执行一下”,如果有异常,
	则通过throw向外抛出,随后在外部通过catch捕获并处理异常。
*/
// 简单说了一下抛出异常机制,感兴趣的小伙伴可以去详细的查一下相关资料

Semaphore::~Semaphore() {
    sem_destroy(&m_semaphore);
}

void Semaphore::wait() {
    if(sem_wait(&m_semaphore)) {
        throw std::logic_error("sem_wait error");
    }
}

void Semaphore::notify() {
    if(sem_post(&m_semaphore)) {
        throw std::logic_error("sem_post error");
    }
}

下面是线程类的函数实现

namespace sylar {
// 当前线程的指针
static thread_local Thread* t_thread = nullptr;
static thread_local std::string t_thread_name = "UNKNOW";
// thread_local变量,就是线程局部变量,意味着这个变量是线程独有的,
// 是不能与其他线程共享的。这样就可以避免资源竞争带来的多线程的问题。

// static sylar::Logger::ptr g_logger = SYLAR_LOG_NAME("system");
// 源码这里是使用作者前面实现的log类来打印日志调试的,
// 我在调试时没有使用作者的这个log类,就只使用了std::cout来打印调试信息。

//  获取当前线程指针
Thread* Thread::GetThis() {
    return t_thread;
}
// 获取当前线程名称
const std::string& Thread::GetName() {
    return t_thread_name;
}
// 设置当前线程姓名
void Thread::SetName(const std::string& name) {
    if(name.empty()) {
        return;
    }
    if(t_thread) {
        t_thread->m_name = name;
    }
    t_thread_name = name;
}
// 构造函数
Thread::Thread(std::function<void()> cb, const std::string& name)
    :m_cb(cb)
    ,m_name(name) {
    if(name.empty()) {
        m_name = "UNKNOW";
    }
    int rt = pthread_create(&m_thread, nullptr, &Thread::run, this);
    /*	pthread_create的用法
		int pthread_create(pthread_t *thread, 
			const pthread_attr_t *attr,
			void *(*start_routine) (void *), void *arg);
		第一个参数用于存放指向pthread_t类型的指针(指向该线程tid号的地址)
		第二个参数表示了线程的属性,一般以NULL表示默认属性
		第三个参数是一个函数指针,就是线程执行的函数。这个函数返回值为 void*,形参为 void*
		第四个参数则表示为向线程处理函数传入的参数,若不传入,可用 NULL 填充
		这里是将类的this指针传递进去
	*/
    if(rt) {
        SYLAR_LOG_ERROR(g_logger) << "pthread_create thread fail, rt=" << rt
            << " name=" << name;
        throw std::logic_error("pthread_create error");
    }
    m_semaphore.wait();
    // 消耗一个信号量,需要配合释放信号量一同使用,否则其他进程将会阻塞等待信号量
}

Thread::~Thread() {
    if(m_thread) {
        pthread_detach(m_thread);
        //线程分离状态:指定该状态,线程主动与主控线程断开关系。
        //线程一旦终止就立刻回收它占用的所有资源,而不保留终止状态。
    }
}

void Thread::join() {
    if(m_thread) {
        int rt = pthread_join(m_thread, nullptr);
        /*	当A线程调用线程B并 pthread_join() 时,
        	A线程会处于阻塞状态,直到B线程结束后,A线程才会继续执行下去。
        	当 pthread_join() 函数返回后,被调用线程才算真正意义上的结束,
        	它的内存空间也会被释放(如果被调用线程是非分离的)。
        */
        if(rt) {
            SYLAR_LOG_ERROR(g_logger) << "pthread_join thread fail, rt=" << rt
                << " name=" << m_name;
            throw std::logic_error("pthread_join error");
        }
        m_thread = 0;
    }
}

// 线程的执行体
void* Thread::run(void* arg) {
    // arg为函数体的执行参数
    Thread* thread = (Thread*)arg;
    
    t_thread = thread;
    t_thread_name = thread->m_name;
    //thread->m_id = sylar::GetThreadId();
    thread->m_id = syscall(SYS_gettid);
    pthread_setname_np(pthread_self(), thread->m_name.substr(0, 15).c_str());

    std::function<void()> cb;
    cb.swap(thread->m_cb);// 获得执行函数

    thread->m_semaphore.notify(); // 释放信号量

    cb(); // 在这里才是真正的执行
    return 0;
}
}

下面为测试程序

void fun1() {
    std::cout << "[name] " << sylar::Thread::GetName()
                << ", [this.name] " << sylar::Thread::GetThis()->GetName()
                << ", [id] " << syscall(SYS_gettid)
                << ", [this.id] " << sylar::Thread::GetThis()->getId()
                << std::endl;
}

int main(int argc, char** argv) {
    std::cout << "[thread test begin]" << std::endl;

    std::vector<sylar::Thread::ptr> thrs;
    for(int i = 0; i < 5; ++i) {
        sylar::Thread::ptr thr(new sylar::Thread(&fun1, "name_" + std::to_string(i * 1)));

        thrs.push_back(thr);


    }

    for(size_t i = 0; i < thrs.size(); ++i) {
        thrs[i]->join();
    }
    std::cout << "[thread test end]" << std::endl;
    return 0;
}

执行结果为
在这里插入图片描述
不加信号量的执行结果为
在这里插入图片描述
在这里插入图片描述

可以看到,没有按照线程的创建顺序执行。

总结

  1. 本文是我第一次分享C++学习的一些笔记,内容中可能有错误的地方,还请多包涵
  2. 后续在空闲时间将把sylar服务器框架给分享完毕
  3. 目标是使用sylar框架设计一个mmo类型的多人游戏服务器
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值