sylar库学习线程与协程
知识点
thread_local关键字,这个关键字在我理解看来是一个类似于static的关键字,但区别在于这个关键字所描述的数据的划分是线程,在单个线程内它与static没有区别,而不同的线程中thread_local关键字是独立的每个线程都有一份单独的,互不干扰的数据。因此这一关键字是构造多线程调度的关键,在线程类,协程类,调度器类都需要用到。
其中线程和线程执行其他函数之前,都是用一个函数将其他函数包装起来,以此来设置函数执行之前的参数
线程
由于使用的是Linux作为操作系统,要将程序中所得到的线程号与系统中使用ps命令查出来的编号相对应,最好使用linux系统中自带的库。也就是pthread
线程类主要是包装了
pthread_create(thread)
pthread_detach(~thread)
pthread_join(join)
pthread_setname_np(run)
这几个函数,这些都是C语言风格的函数,以pthread_t数据结构来进行函数的控制。
其中为了构造函数之前
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); //线程创建,此处开始就立即执行
// 线程可能在构造函数返回之前就跑起来
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();//可以等到跑起来在唤醒
}
void *Thread::run(void *arg) {
//线程函数参数必须是void* POSIX线程库标准,run为静态成员,this指针必须显式传入
Thread* thread = (Thread *)arg;
t_thread = thread;
t_thread_name = thread->m_name;
thread->m_id = sylar::GetThreadId();
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;
}
通过加了一个锁保证了执行顺序的问题,构造函数一直在等待,知道设置完函数,设置完名字,开始运行函数的时候才能析构完成
callback一下,其中的线程指针和线程名就是使用的thread_local来进行区分的。
协程
相比于线程只是运行一个函数来说,协程就是一个讲究人啊。其中设定了状态(设计模式中就有一个状态模式,咱这也是使用了设计模式的人上人了)。
协程的包装主要是控制的ucontext_t数据结构
其中
需要有三个参数需要设置
uc_link//后续上下文,一般置空指针
uc_stack.ss_sp//栈空间地址->对应malloc申请内存大小
uc_stack.ss_size//栈空间大小
包装有
getcontext //初始化,设置值(Fiber)
makecontext//类似于执行函数(Fiber或reset)
swapcontext//上下文切换,非常重要的函数。协程通过这个函数进行切换
其中总体结构为,设置一个主协程,在使用的时候通过主协程来进行控制
协程 <——>主协程<——>协程
每个线程中需要一个主协程在中间进行调度。
因此 thread_local关键字又开始派上用场了,
static thread_local Fiber *t_fiber = nullptr;//当前执行线程
static thread_local Fiber