mysql是如何重用线程的呢?首先需要说明一下几个与此密切相关的全局变量的含义:
- thread_count:当前线程数目,初始为0,由LOCK_thread_count保护
- wake_thread:初始为0
- cached_thread_count:初始为0,记录此时有多少线程被cached了
一个客户端连接请求和关闭的过程中,与创建、关闭线程密切相关的流程如下:
在附件中。
那么这里有几种情况:
1.不断处理客户端请求,没有客户端断开:
这种情况就不需要重用线程,因为没有客户端断开,因此没有线程被cached.因此每次响应客户端也就必须要创建新的线程.
这种情况总是有个度的,与我们设置的max_connection相关.
2.客户端请求与断开交替进行:
我们根据上图来分析,假设此时有n个线程在响应各自客户端的请求,假设接着4号线程的网络连接断开了,那么首先它要锁住thread_count,并且将其值减一,但此时并不立即释放thread_count的互斥锁:
pthread_mutex_lock(&LOCK_thread_count);
thread_count--;
接着调用cache_thread()来cache本线程,注意,这个时候如果有客户连接请求,是无法获得线程的(无论创建新的还是重用旧的),原因见上图”处理新客户连接请求”的开头,需要获得thread_count的互斥锁,但此锁刚刚已经被上锁了还未释放.
在cache_thread()函数中,关键部分:
1.cached_thread_count++;
2.while(...&&!wake_thread&&...)
3. pthread_cond_wait(&COND_thread_cache,&LOCK_thread_count);
4.cached_thread_count--;
5.if(wake_thread)
6.{
7. wake_thread--;
8. 新建一个THD,并将其与刚刚唤醒的thread绑定
9.}
先是将cached_thread_count自增1,代表多了一个cached的线程,接着while(…),由于wake_thread初值为0,因此调用第3行,这是linux下条件变量相关的一个api.
这个函数将执行:
1.释放LOCK_thread_count互斥锁(还记得不,刚刚这个互斥锁锁住之后还没释放呢),接着阻塞本线程,等待COND_thread_cache条件的发生.
2.好了,现在4号线程释放了LOCK_thread_count,并且阻塞了自己,等待COND_thread_count条件的发生.那么这个时候接下去会发生两种可能:
假设1:这时如果线程1也要关闭网络连接了,那么由于第1步中的pthread_cond_wait(…,&LOCK_thread_count)的调用,thread_count的互斥锁已经释放了,所以线程1也可以安全得进行到这一步,也是调用阻塞等待COND_thread_cache条件的发生,这时候cached_thread_count的值是2了.这种情况可以一直下去,cached_thread_count的值会不断增加,代表着缓存住的thread的不断增多,每缓存完一个线程,thread_count的互斥锁LOCK_thread_count都会被释放,但是在缓存一个thread的时候,这个互斥锁一直被锁着,也就是说不可能有多个线程在同时缓存自己,必须one by one.
假设2.如果这时候新来一个客户端请求,那么首先,获得LOCK_thread_count锁:
pthread_mutex_lock(&LOCK_thread_count);
thread_count++;
接着检查cached_thread_count > wake_thread是否满足,由于目前为止wake_thread一直为0,而4号线程在进入阻塞之前已经将cached_thread_count自增了,因此满足条件,执行if为真时的语句:
if( cached_thread_count > wake_thread)
{
wake_thread++;
pthread_cond_signal(*COND_thread_cache);
}
else
{
创建新线程
}
pthread_mutex_unlock(&LOCK_thread_count);
这个操作先是将wake_thread自增(此时变为1),再发送COND_thread_cache信号,最后释放LOCK_thread_count锁.
嘿,还记得不,刚才4号线程正在等待这个COND_thread_cache条件的发生了,于是4号线程醒了过来,这时4号线程再次锁住LOCK_thread_count锁(这是由pthread_cond_wait()函数所规定的),接着while()检查!wake_thread已经不满足条件了(这里我有个疑惑,为什么要用while()呢,直接用一个if不就可以了吗?),跳出循环,cached_thread_count减一,代表着减少了一个cache thread了,接着进入if语句,先是将wake_thread减1(又变成0了),再新建一个THD,并将其与刚刚唤醒的thread绑定,这里的内容就不展开了.最后释放LOCK_thread_count锁.
重用了的线程其内部的参数信息被更新为当前连接的客户端的信息,而线程本身却还是没有改变.
最后通过一步步地返回,回到了handle_one_connection中的那个for(;;)循环中,再次do_command();继续一步步地轮回.
我的理解大致就是这样一个过程.thread_count的互斥锁LOCK_thread_count在这个过程中扮演了重要的角色.
cached_thread_count和wake_thread也是,前者代表着”缓存池中有多少个thread”.后者在我目前的理解来说,更像是一个flag,对这个wake_thread,我还不是理解的很透彻.
原文发表在这里,非常欢迎讨论与拍砖~