BOOST的Thread库博大精深,有很多的多线程概念在里面,今天就对其中很多call都用到的interrupt_wait的实现进行解释。首先,在BOOST文档中,有1个不得不看的概念,如下:
The following functions are interruption points, which will throw boost::thread_interrupted
if interruption is enabled for the current thread, and interruption is requested for the current thread:
boost::thread::join()
boost::thread::timed_join()
boost::condition_variable::wait()
boost::condition_variable::timed_wait()
boost::condition_variable_any::wait()
boost::condition_variable_any::timed_wait()
boost::thread::sleep()
boost::this_thread::sleep()
boost::this_thread::interruption_point()
这个是说,有这么多的call可以用interrupt来打断,既有纯打断(属于无所事事,就想打断型)- interruption_point; 又有在休眠等待中被打断(从睡梦中惊醒)- 如其他。下面来看看官方的interrupt的描述
interrupt()
void interrupt();
If *this
refers to a thread of execution, request that the thread will be interrupted the next time it enters one of the predefined interruption points with interruption enabled, or if it is currently blocked in a call to one of the predefined interruption points with interruption enabled .
可以看出,实际上就是分上面的2种打断类型。马不停蹄,我们继续往下看:
interrupt原理:
到底interrupt这种高级功能是怎么实现的呢,the answer is "Event handle and throw thread_interrupted();" 即抛出一个特定的异常,来看段源码:
unsigned __stdcall thread_start_function(void* param)
{
detail::thread_data_base* const thread_info(reinterpret_cast<detail::thread_data_base*>(param));
set_current_thread_data(thread_info); //在tls中设置thread 的info,一会会介绍到
try
{
thread_info->run();
}
catch(thread_interrupted const&) //看见没,在线程入口函数中catch interruption, 这里也明确个概念,打断可不是中断,即中断是是一般意义上的暂停,只是暂时停住,后面还会执行,而打断就是中止,到此为止的意思。这里的实现是利用异常来跳出线程函数执行体。
{
}
// Removed as it stops the debugger identifying the cause of the exception
// Unhandled exceptions still cause the application to terminate
// catch(...) //可惜啊,其他的异常可不管,直接挂掉
// {
// std::terminate();
// }
run_thread_exit_callbacks(); //从当前的tls中取出exit的函数
return 0;
}
在start_thread中调用上面的入口函数,主要代码如下:
void thread::start_thread()
{
uintptr_t const new_thread=_beginthreadex(0,0,&thread_start_function,thread_info.get(),CREATE_SUSPENDED,&thread_info->id);
if(!new_thread)
{
throw thread_resource_error();
}
intrusive_ptr_add_ref(thread_info.get());
thread_info->thread_handle=(detail::win32::handle)(new_thread);
ResumeThread(thread_info->thread_handle);
}
下面我们看看 detail::thread_data_base* const thread_info中究竟隐藏了些什么:
struct thread_data_base
{
long count; //intrusive count
detail::win32::handle_manager thread_handle; //thread handle
detail::win32::handle_manager interruption_handle; //interruption event handle
boost::detail::thread_exit_callback_node* thread_exit_callbacks; //as its name say
boost::detail::tss_data_node* tss_data; //特定的退出函数的结构
bool interruption_enabled; //是否enable interruption
unsigned id; // thread id
}
可见那是相当的丰富,其中interruption_handle和interruption_enable是我们要描述的重点。再来看,
void thread_data_base::interrupt()
{
BOOST_VERIFY(detail::win32::SetEvent(interruption_handle)!=0);
}
thread的interrupt实际是调用上面thread_data_base中的函数,而这个函数则给interruption_handle event置位了,即通知说interrupt事件发生了。有通知必有等待通知的同志们。介绍如下,干将:
bool interruptible_wait(detail::win32::handle handle_to_wait_for,detail::timeout target_time)
{
detail::win32::handle handles[3]={0}; //分别记录thread handle、interruption handle 和 time handle
unsigned handle_count=0;
unsigned wait_handle_index=~0U; //指向thread handle
unsigned interruption_index=~0U; //指向interruption handle
unsigned timeout_index=~0U; //指向time handle
if(handle_to_wait_for!=detail::win32::invalid_handle_value) //判断是否需要等待线程结束
{
wait_handle_index=handle_count;
handles[handle_count++]=handle_to_wait_for;
}
if(get_current_thread_data() && get_current_thread_data()->interruption_enabled) //判断是否enable了interruption
{
interruption_index=handle_count;
handles[handle_count++]=get_current_thread_data()->interruption_handle;
}
detail::win32::handle_manager timer_handle;
#ifndef UNDER_CE
unsigned const min_timer_wait_period=20;
if(!target_time.is_sentinel()) //判断是否需要有timeout
{
detail::timeout::remaining_time const time_left=target_time.remaining_milliseconds();
if(time_left.milliseconds > min_timer_wait_period)
{
// for a long-enough timeout, use a waitable timer (which tracks clock changes)
timer_handle=CreateWaitableTimer(NULL,false,NULL);
if(timer_handle!=0)
{
LARGE_INTEGER due_time=get_due_time(target_time);
bool const set_time_succeeded=SetWaitableTimer(timer_handle,&due_time,0,0,0,false)!=0; //设置TIMER
if(set_time_succeeded)
{
timeout_index=handle_count;
handles[handle_count++]=timer_handle;
}
}
}
else if(!target_time.relative)
{
// convert short absolute-time timeouts into relative ones, so we don't race against clock changes
target_time=detail::timeout(time_left.milliseconds);
}
}
#endif
bool const using_timer=timeout_index!=~0u;
detail::timeout::remaining_time time_left(0);
do
{
if(!using_timer)
{
time_left=target_time.remaining_milliseconds();
}
if(handle_count)
{
unsigned long const notified_index=detail::win32::WaitForMultipleObjects(handle_count,handles,false,using_timer?INFINITE:time_left.milliseconds); //关键到来,等待根据前面的判断需要可选thread结束,打断,及其是否有超时,其实这个就是上文说的等待interrupt通知的同志。
if(notified_index<handle_count)
{
if(notified_index==wait_handle_index) //等到thread 结束事件
{
return true;
}
else if(notified_index==interruption_index)//等到interrupt事件
{
detail::win32::ResetEvent(get_current_thread_data()->interruption_handle); //将interrupt event 复位,代表一次打断结束,下次您再重新来打断,一般说来就只能打断一次,因为已经跳出函数体了嘛,不排除在线程退出函数中再搞次
throw thread_interrupted(); //抛出表示打断的异常,跳出线程执行体
}
else if(notified_index==timeout_index) //等到timeout 事件
{
return false;
}
}
}
else
{
detail::win32::Sleep(time_left.milliseconds);
}
if(target_time.relative)
{
target_time.milliseconds-=detail::timeout::max_non_infinite_wait;
}
}
while(time_left.more);
return false;
}
如sleep,wait函数内部都用到了这个interrupt_wait. 此外,还有一对打断的通知和被通知者:
void interruption_point()
{
if(interruption_enabled() && interruption_requested())
{
detail::win32::ResetEvent(get_current_thread_data()->interruption_handle);
throw thread_interrupted();
}
}
bool interruption_requested()
{
return get_current_thread_data() && (detail::win32::WaitForSingleObject(get_current_thread_data()->interruption_handle,0)==0);
}
当判断是否请求了interrupt后(调用interruption_requested来判断),如果请求了interrupt,则调用interruption_point可以直接打断线程,是为打断点。第二种打断类型。
PostScript:
void yield()
{
detail::win32::Sleep(0);
}
这个sleep(0)能让线程放弃剩余时间片,主动让操作系统调度线程,呵呵。
累啊,今天就到这,下次继续~嘿嘿