processor_t结构体,声明了一些公用方法:
get_total_threads获取总的线程数量;
get_idle_threads获取空闲线程数量;
get_working_threads按指定的优先级获取处理该优先级的job的线程数量;
get_job_load 或取指定优先级job队列中的job数量;
queue_job 添加job到指定job优先级对应的队列中;
execute_job直接调用一个空闲线程处理job,如果没有空闲线程则直接由调用该接口的线程直接处理job。
set_threads 设置线程池中的线程数量,这可能导致线程池的扩大或缩小。
cancel 线程数量设置为0,取消线程池,直到所有job处理完毕。
destroy 销毁processor对象。
private_processor_t 结构体继承processor_t结构体,
processor_create函数创建一个processor_t对象,其中挂载公有函数初始化processor_t对象,除此之外,private_processor_t还有私有的成员:
total_threads 正在运行的工作线程数量,他们可能处于等待job_added条件变量中或正在处理job;
desired_threads 用户指定的线程数量;set_threads 设置的线程数量会保存。
working_threads数组,每个优先级的线程数量;
threads 链表,保存线程池中所有的线程;
jobs数组,每个优先级的job链表;
prio_threads数组,每个优先级指定的线程数量;
mutex互斥锁
job_added条件变量,加入job通知,线程从条件变量处继续执行,从队列中取job执行。
thread_terminated 线程结束条件变量。线程函数退出时发送该信号,上述cancel函数接收该信号。
worker_thread_t结构体描述一个工作线程:
processor 指向实例化的processor对象;
thread 指向工作线程;
job 指向将要被该线程处理的job;
priority job的优先级。
get_idle_threads_nolock函数:
total_threads 总的线程数减去正在执行job的线程数量。
get_job函数:
值越小,优先级越高,从最高优先级的job开始,避让算法,prio_threads[i]大于working_threads[i]说明该i级别的job处理线程不够,reserved保存缺口数量,在for循环执行到下一个优先级的job时,reserved >= idle的化就直接退出不处理,优先高级别的job。跳过这些能get了,就remove_first从该优先级的job链表中取出第一个job。
process_jobs函数:
是线程池多有线程的执行函数。
while (this->desired_threads >= this->total_threads)决定是否跳出循环结束线程,当不成立时说明desired_threads 变小了,线程池缩小了,要把执行完job的线程退出。否则则取出worker线程优先级一致的job,调用process_job处理job,注意互斥锁的加锁与解锁,入时解锁处理job,出时加锁继续下一次的保护。没有job则条件等待。由于条件等待不超时,所以线程池的缩放需要加入job的信号激活。
那么当此线程退出时,发生了线程池缩小,total_threads线程数减少,发送thread_terminated条件信号。在cancel函数中接收判断线程是否都已经释放了:
c
while (this->total_threads > 0)
{
this->job_added->broadcast(this->job_added);
this->thread_terminated->wait(this->thread_terminated, this->mutex);
}
process_job函数处理job:
(1) working_threads++ 增加总的在处理job数
(2) 由于thread_cleanup_pop(FALSE);纯如false,不必理会
(3)调用job->execute的处理job,返回值的几种类型:
若是JOB_REQUEUE_TYPE_NONE,则!= JOB_REQUEUE_TYPE_DIRECT,跳出循环,后续to_destroy = worker->job; 进而to_destroy->destroy(to_destroy);销毁job。
若是JOB_REQUEUE_TYPE_DIRECT,则是再直接执行job->execute,前提是worker->job->cancel不为空,也即可取消,否则while中一直执行不可取消了。
若是JOB_REQUEUE_TYPE_FAIR,后续插入job队列。
若是JOB_REQUEUE_TYPE_SCHEDULE,则...
JOB_REQUEUE_TYPE_DIRECT:
(4)调用job->execute后,working_threads--
(5)如果在job->execute时,worker->job->status 设置为 JOB_STATUS_CANCELED,则销毁job。
queue_job函数:
按不同的优先级插入job,并向线程发送条件变量信号激发处理。
execute_job:
如果有空闲线程,插入和job相同优先级的队列的首部,唤醒等待此条件变量的线程,
如果插入队列没有成功,则由调用此函数的线程直接处理job的execute函数
set_threads:
(1)根据配置预设各优先级job的处理线程数量。
(2)根据要设置的count线程数,扩充或缩小线程池。
(3)缩小,则this->desired_threads = count;,在process_jobs函数中,处理完job后会while条件判断失败,退出循环,退出该线程,达到减少目的。
(4)扩大,则thread_create创建新的线程。
线程局部存储变量private_thread_t :
c
//每个线程的id、cleanup_handlers之类在不同的存储空间,互不影响。
thread_t *thread_current()
{
private_thread_t *this;
this = (private_thread_t*)current_thread->get(current_thread);
if (!this)
{
this = thread_create_internal();
this->id = get_thread_id();
current_thread->set(current_thread, (void*)this);
}
return &this->public;
}
u_int thread_current_id()
{
private_thread_t *this = (private_thread_t*)thread_current();
return this ? this->id : 0;
}
void thread_cleanup_push(thread_cleanup_t cleanup, void *arg)
{
private_thread_t *this = (private_thread_t*)thread_current();
cleanup_handler_t *handler;
INIT(handler,
.cleanup = cleanup,
.arg = arg,
);
this->cleanup_handlers->insert_last(this->cleanup_handlers, handler);
}
(1)在多线程编程中,pthread_setspecific 和 pthread_getspecific 是两个用于处理线程局部存储(TLS)的函数,
(2)它们允许线程存储和访问线程特定的数据。这些函数提供了一种在同一线程中不同函数间共享数据的方法,这对于创建线程安全的程序至关重要。
(3)线程局部存储是一种允许数据在多个线程间独立存在的机制。每个线程都有自己的数据副本,因此对这些数据的操作不会影响其他线程。
(4)这种机制在处理不支持原子操作的自定义数据类型时尤其有用,因为它可以在不使用锁的情况下实现线程安全。
(5)要使用这些函数,首先需要通过 pthread_key_create 创建一个 pthread_key_t 类型的键。这个键用于标识线程特定的数据。
(6)然后,可以使用 pthread_setspecific 将数据与键关联,使用 pthread_getspecific 获取与键关联的数据。
(7)在上述代码中,pthread_setspecific 用于设置线程特定的数据,而 pthread_getspecific 用于获取这些数据。
(8)每个线程都有自己的数据副本,因此即使是使用相同的键,不同线程中的数据也是独立的。