我以前写线程时要么老老实实照着声明写,要么使用C++类的静态成员函数来作为回调函数,经常会因为线程代码而破坏封装.之前虽然知道类成员函数的展开形式,但从没想过利用过它,昨天看深入ATL时无意中学会了这一招:)
类成员方法是一个比较特殊的函数,它在编译时会被转化成普通函数,比如有TMyClass类:
class TMyClass
{
void Func();
};
这个TMyClass::Func最终会转化成 void Func(TMyClass *this); 也就是说在原第一个参数前插入指向对象本身的this指针。
我们可以利用这个特性写一个非静态类成员方法来直接作为线程回调函数,先看_beginthread函数的定义:
unsigned long _RTLENTRY _EXPFUNC _beginthread (void (_USERENTRY *__start)(void *),unsigned __stksize, void *__arg);
其中的第一个参数就是作为线程执行主体的回调函数。它的原型是:void Func(void *),这个void*参数是作为自定义数据传入的。对比一下上面所说的TMyClass::Func的最终形式,它正好可以符合这里的要求。
现在做个实验:
#include <stdio.h>
#include <process.h>
class TMyClass
{
int m_nCount;
int m_nId;
public:
TMyClass(int nId,int nCount)
:m_nId(nId),m_nCount(nCount)
{ }
void _USERENTRY ThreadProc() // 类成员方法
{
for(int i=0; i<m_nCount; i++) // 根据m_nCount成员打印一排数字
{
printf("Class%d : %d\n",m_nId,i);
}
}
};
int main(int argc, char* argv[])
{
// 联合类,用于转换类成员方法指针到普通函数指针(试过编译器不允许在这两种函数之间强制转换),不知道有没有更好的方法。
union {
void (_USERENTRY *ThreadProc)(void *);
void (_USERENTRY TMyClass::*MemberProc)();
} Proc; // 尽管联合里的两种函数类型现在看起来有很大不同,但它们的最终形式是相同的。
TMyClass MyClass1(1,10),MyClass2(2,5); // 产生两个TMyClass对象
Proc.MemberProc = &TMyClass::ThreadProc; // 转换,Proc.ThreadProc就是对应的普通函数指针了
_beginthread(Proc.ThreadProc,4096,&MyClass1); // 开始线程,这里的Proc.ThreadProc实际上是TMyClass::ThreadProc, 它要的this指针是我们给的&MyClass1。
_beginthread(Proc.ThreadProc,4096,&MyClass2);
system("pause");
return 0;
}
运行!神奇吧?:-)
其实不止线程回调函数,其实只要是形如Func(void*,...)的回调函数都可以用这种方法直接使用类成员方法。(前提是第一个void*是自定义数据,也就是说它不能有其它功能)。
#!/usr/bin/python
#wangben updated 20130108
class WAND:
'''implement wand algorithm'''
def __init__(self, InvertIndex, last_docid):
self.invert_index = InvertIndex #InvertIndex: term -> docid1, docid2, docid3 ...
self.current_doc = 0
self.current_invert_index = {}
self.query_terms = []
self.threshold = 2
self.sort_terms = []
self.LastID = 2000000000 #big num
self.debug_count = 0
self.last_docid = last_docid
def __InitQuery(self, query_terms):
'''check terms len > 0'''
self.current_doc = -1
self.current_invert_index.clear()
self.query_terms = query_terms
self.sort_terms[:] = []
self.debug_count = 0
for term in query_terms:
#initial start pos from the first position of term's invert_index
self.current_invert_index[term] = [ self.invert_index[term][0], 0 ] #[ docid, index ]
def __SortTerms(self):
if len(self.sort_terms) == 0:
for term in self.query_terms:
if term in self.current_invert_index:
doc_id = self.current_invert_index[term][0]
self.sort_terms.append([ int(doc_id), term ])
self.sort_terms.sort()
def __PickTerm(self, pivot_index):
return 0
def __FindPivotTerm(self):
score = 0
for i in range(0, len(self.sort_terms)):
score += 1
if score >= self.threshold:
return [ self.sort_terms[i][1], i]
return [ None, len(self.sort_terms) ]
def __IteratorInvertIndex(self, change_term, docid, pos):
'''move to doc id > docid'''
doc_list = self.invert_index[change_term]
i = 0
for i in range(pos, len(doc_list)):
if doc_list[i] >= docid:
pos = i
docid = doc_list[i]
break
return [ docid, pos ]
def __AdvanceTerm(self, change_index, docid ):
change_term = self.sort_terms[change_index][1]
pos = self.current_invert_index[change_term][1]
(new_doc, new_pos) = \
self.__IteratorInvertIndex(change_term, docid, pos)
self.current_invert_index[change_term] = \
[ new_doc , new_pos ]
self.sort_terms[change_index][0] = new_doc
def __Next(self):
if self.last_docid == self.current_doc:
return None
while True:
self.debug_count += 1
#sort terms by doc id
self.__SortTerms()
#find pivot term > threshold
(pivot_term, pivot_index) = self.__FindPivotTerm()
if pivot_term == None:
#no more candidate
return None
#debug_info:
for i in range(0, pivot_index + 1):
print self.sort_terms[i][0],self.sort_terms[i][1],"|",
print ""
pivot_doc_id = self.current_invert_index[pivot_term][0]
if pivot_doc_id == self.LastID: #!!
return None
if pivot_doc_id <= self.current_doc:
change_index = self.__PickTerm(pivot_index)
self.__AdvanceTerm( change_index, self.current_doc + 1 )
else:
first_docid = self.sort_terms[0][0]
if pivot_doc_id == first_docid:
self.current_doc = pivot_doc_id
return self.current_doc
else:
#pick all preceding term
for i in range(0, pivot_index):
change_index = i
self.__AdvanceTerm( change_index, pivot_doc_id )
def DoQuery(self, query_terms):
self.__InitQuery(query_terms)
while True:
candidate_docid = self.__Next()
if candidate_docid == None:
break
print "candidate_docid:",candidate_docid
#insert candidate_docid to heap
#update threshold
print "debug_count:",self.debug_count
if __name__ == "__main__":
testIndex = {}
testIndex["t1"] = [ 0, 1, 2, 3, 6 , 2000000000]
testIndex["t2"] = [ 3, 4, 5, 6, 2000000000 ]
testIndex["t3"] = [ 2, 5, 2000000000 ]
testIndex["t4"] = [ 4, 6, 2000000000 ]
w = WAND(testIndex, 6)
w.DoQuery(["t1", "t2", "t3", "t4"])输出结果中会展示next中循环的次数,以及最后被选为candidate的docid