原本想把boost里面的线程模块完全移植到Symbian S60 Fp2中,可惜内核同步的几个核心结构竟然不支持超时。没有超时概念,信号量,条件变量,以及与超时相关的锁用处就不大。同时Symbian不建议采用多线程,会加快消耗电量等等。但是AO还是有不少限制的,我还记得当初初学Windows程序设计时,把一些长任务用定时器或者用系统提供的IdleMessage进行分解的痛苦。Symbian 不建议使用线程,但不是不能使用线程,而且现在手机CPU强劲,电量都是锂电池,适量的使用线程应该没那么糟。
AO 限制:
1:长时间的AO会导致界面阻塞,也就是说界面不刷新了。
2:AO和按键事件在一个线程里面,也就是说AO如果长时间运行,按键事件响应将有一定时间的停滞感。
3:AO的执行过程分解需要耗费大量的程序员心力,而且在某些时候无法确定某些代码的预测执行时间,比如网络,比如磁盘扫描等等情况将导致任务的分解是无法细化。
基于上述原因,AO并不能完全代替线程。线程的使用,将不再遇到细分任务的麻烦,使程序员的精力更多关注于逻辑而不是分解技巧。设计线程的目标是:
1:它能和界面脱离。
2:它是安全的,确保细节错误不重复发生。
3:它在一个界面程序中,数量是可控制的。
4:线程执行完毕,将资源自动归还。
5:降低开发难度,感觉好像没用线程。
传统的开线程过程大概是这样的:
全局入口函数 :返回类型 函数名 (LPVOID pData)
{
对pData 转型 到原类型对象指针 pOrg;
pOrg->.....();....
delete pOrg;?
}
界面响应,网络事件,电话事件.....回调处启动线程
{
RThread curThread;
curThread.Create( ×××××,全局构造函数,传入参数......);
}
.................
上面的代码其中有好几个地方容易犯错:
1:线程创建函数本身,参数那么多,怎么记得住?
2:全局函数。 每写一个线程就要搞一个线程出来。
3:线程执行的全局函数和对象本身分离。至少,转型之后C++编译器就会阻止你访问私有成员变量和成员函数。
4:很难控制线程的数量。导致线程过度切换,电量快速的耗费。
所以下面的线程调用方法很具诱惑:
* class ABCDE
* {
* public:
* void Fun()
* {
* std::cout << "Hello stanley" << std::endl;
* }
* };
* ABCDE abc;
thread trd(ABCDE::FUN, abc);
调用之后,Fun函数将在独立的线程中完成。全局函数,CreateThread,参数细节,转型,是否删除对象,完全和Client(thread类使用者无关)。(当然这还不能保证只创建唯一线程,将在下面讲述如何再次包装它。)
我把代码先贴出来,看贴的人看看是否有疑惑,如果有疑惑,我再来写详细的解释。
///简单的函数和对象绑定组件。
namespace ThreadHelper
{
template<class T>
class simple_bind_t
{
public:
typedef void (T::*pFn)();
typedef pFn FunType;
typedef T class_type;
explicit simple_bind_t(pFn fn,T& t) : m_pFun(fn),m_t(t)
{
}
simple_bind_t(const simple_bind_t& bt) : m_pFun(bt.m_pFun),m_t(bt.m_t)
{
}
simple_bind_t<T> operator = (const simple_bind_t& r)
{
m_pFun = r.m_pFun;
m_t = r.m_t;
}
FunType getfun() { return m_pFun; }
class_type* getdata() { return &m_t;}
void operator()()
{
return ( (m_t.*m_pFun)());
}
private:
pFn m_pFun;
T& m_t;
};
template<class T>
simple_bind_t<T> simple_bind( void (T::*fn)(), T& t)
{
return simple_bind_t<T>(fn,t);
}
}
///线程组件
namespace ThreadHelper
{
class simple
{
public:
virtual void operator()()=0;
virtual void wait() = 0;
virtual void started() = 0;
};
namespace helper
{
template<class T>
class thread_param : public simple
{
public:
thread_param(const ThreadHelper::simple_bind_t<T>& threadfunc)
: m_threadfunc(threadfunc)
{
}
void wait() { }
void started() { }
void operator()()
{
m_threadfunc();
}
simple_bind_t<T> m_threadfunc;
};
}
class thread
{
public:
thread();
template<class T>
explicit thread(const simple_bind_t<T>& threadFunc,const string& name);
~thread();
bool operator==(const thread& other) const;
bool operator!=(const thread& other) const;
void join();
unsigned int id() const;
private:
static int thread_proxy(void *param);
private:
int m_thread;
unsigned int m_id;
bool m_joinable;
};
inline thread::thread() : m_joinable(false)
{
m_id = RThread().Id();
m_thread = RThread().Handle();
}
template<class T>
inline thread::thread(const simple_bind_t<T>& threadFunc,const string& name) : m_joinable(true)
{
helper::thread_param<T>*param = new helper::thread_param<T>(threadFunc);
RThread curThread;
curThread.Create( name.DesL(),
thread_proxy,
4096,
NULL,
param);
m_id = curThread.Id();
m_thread = curThread.Handle();
curThread.Resume();
}
inline thread::~thread()
{
if (m_joinable)
{
RThread curThread;
curThread.SetHandle(m_thread);
curThread.Close();
}
}
inline bool thread::operator==(const thread& other) const
{
return other.m_id == m_id;
}
inline bool thread::operator!=(const thread& other) const
{
return !operator==(other);
}
inline void thread::join()
{
if(m_joinable)
{
RThread curThread;
curThread.SetHandle(m_thread);
TRequestStatus status;
curThread.Logon(status);
}
m_joinable = false;
}
}
///一处定义到处使用的全局函数,用户不必写的。
namespace ThreadHelper
{
void do_thread_proxy(void* param)
{
ThreadHelper::simple* p = static_cast<ThreadHelper::simple*>(param);
if(p)
{
(*p)();
delete p;
}
}
int thread::thread_proxy(void* param)
{
CTrapCleanup* cleanupStack = CTrapCleanup::New();
TRAPD(error,do_thread_proxy(param));
delete cleanupStack;
return 0;
}
}
最终:thread trd(simple_bind(&ABCDE::FUN, abc));
当然,symbian 下使用线程和windows下面情况不同,比如你在主线程创建的一些Session 子线程没法使用,这种情况还得视系统而定。很明显,程序员还是要花费一定的心力来确保一些东西能够正确运作。我的意见是界面以及以及按键等事件的处理放到主线程处理,后台工作线程和主线程仅仅有数据的交互。也就是说,后台线程准备好数据之后,让主线程使用,主线程在接收到事件指示后发送命令给子线程。按照这样的逻辑就能够达到界面高响应的目标。
1:长时间的AO会导致界面阻塞,也就是说界面不刷新了。
2:AO和按键事件在一个线程里面,也就是说AO如果长时间运行,按键事件响应将有一定时间的停滞感。
3:AO的执行过程分解需要耗费大量的程序员心力,而且在某些时候无法确定某些代码的预测执行时间,比如网络,比如磁盘扫描等等情况将导致任务的分解是无法细化。
基于上述原因,AO并不能完全代替线程。线程的使用,将不再遇到细分任务的麻烦,使程序员的精力更多关注于逻辑而不是分解技巧。设计线程的目标是:
1:它能和界面脱离。
2:它是安全的,确保细节错误不重复发生。
3:它在一个界面程序中,数量是可控制的。
4:线程执行完毕,将资源自动归还。
5:降低开发难度,感觉好像没用线程。
传统的开线程过程大概是这样的:
全局入口函数 :返回类型 函数名 (LPVOID pData)
{
对pData 转型 到原类型对象指针 pOrg;
pOrg->.....();....
delete pOrg;?
}
界面响应,网络事件,电话事件.....回调处启动线程
{
RThread curThread;
curThread.Create( ×××××,全局构造函数,传入参数......);
}
.................
上面的代码其中有好几个地方容易犯错:
1:线程创建函数本身,参数那么多,怎么记得住?
2:全局函数。 每写一个线程就要搞一个线程出来。
3:线程执行的全局函数和对象本身分离。至少,转型之后C++编译器就会阻止你访问私有成员变量和成员函数。
4:很难控制线程的数量。导致线程过度切换,电量快速的耗费。
所以下面的线程调用方法很具诱惑:
* class ABCDE
* {
* public:
* void Fun()
* {
* std::cout << "Hello stanley" << std::endl;
* }
* };
* ABCDE abc;
thread trd(ABCDE::FUN, abc);
调用之后,Fun函数将在独立的线程中完成。全局函数,CreateThread,参数细节,转型,是否删除对象,完全和Client(thread类使用者无关)。(当然这还不能保证只创建唯一线程,将在下面讲述如何再次包装它。)
我把代码先贴出来,看贴的人看看是否有疑惑,如果有疑惑,我再来写详细的解释。
///简单的函数和对象绑定组件。
namespace ThreadHelper
{
template<class T>
class simple_bind_t
{
public:
typedef void (T::*pFn)();
typedef pFn FunType;
typedef T class_type;
explicit simple_bind_t(pFn fn,T& t) : m_pFun(fn),m_t(t)
{
}
simple_bind_t(const simple_bind_t& bt) : m_pFun(bt.m_pFun),m_t(bt.m_t)
{
}
simple_bind_t<T> operator = (const simple_bind_t& r)
{
m_pFun = r.m_pFun;
m_t = r.m_t;
}
FunType getfun() { return m_pFun; }
class_type* getdata() { return &m_t;}
void operator()()
{
return ( (m_t.*m_pFun)());
}
private:
pFn m_pFun;
T& m_t;
};
template<class T>
simple_bind_t<T> simple_bind( void (T::*fn)(), T& t)
{
return simple_bind_t<T>(fn,t);
}
}
///线程组件
namespace ThreadHelper
{
class simple
{
public:
virtual void operator()()=0;
virtual void wait() = 0;
virtual void started() = 0;
};
namespace helper
{
template<class T>
class thread_param : public simple
{
public:
thread_param(const ThreadHelper::simple_bind_t<T>& threadfunc)
: m_threadfunc(threadfunc)
{
}
void wait() { }
void started() { }
void operator()()
{
m_threadfunc();
}
simple_bind_t<T> m_threadfunc;
};
}
class thread
{
public:
thread();
template<class T>
explicit thread(const simple_bind_t<T>& threadFunc,const string& name);
~thread();
bool operator==(const thread& other) const;
bool operator!=(const thread& other) const;
void join();
unsigned int id() const;
private:
static int thread_proxy(void *param);
private:
int m_thread;
unsigned int m_id;
bool m_joinable;
};
inline thread::thread() : m_joinable(false)
{
m_id = RThread().Id();
m_thread = RThread().Handle();
}
template<class T>
inline thread::thread(const simple_bind_t<T>& threadFunc,const string& name) : m_joinable(true)
{
helper::thread_param<T>*param = new helper::thread_param<T>(threadFunc);
RThread curThread;
curThread.Create( name.DesL(),
thread_proxy,
4096,
NULL,
param);
m_id = curThread.Id();
m_thread = curThread.Handle();
curThread.Resume();
}
inline thread::~thread()
{
if (m_joinable)
{
RThread curThread;
curThread.SetHandle(m_thread);
curThread.Close();
}
}
inline bool thread::operator==(const thread& other) const
{
return other.m_id == m_id;
}
inline bool thread::operator!=(const thread& other) const
{
return !operator==(other);
}
inline void thread::join()
{
if(m_joinable)
{
RThread curThread;
curThread.SetHandle(m_thread);
TRequestStatus status;
curThread.Logon(status);
}
m_joinable = false;
}
}
///一处定义到处使用的全局函数,用户不必写的。
namespace ThreadHelper
{
void do_thread_proxy(void* param)
{
ThreadHelper::simple* p = static_cast<ThreadHelper::simple*>(param);
if(p)
{
(*p)();
delete p;
}
}
int thread::thread_proxy(void* param)
{
CTrapCleanup* cleanupStack = CTrapCleanup::New();
TRAPD(error,do_thread_proxy(param));
delete cleanupStack;
return 0;
}
}
最终:thread trd(simple_bind(&ABCDE::FUN, abc));
当然,symbian 下使用线程和windows下面情况不同,比如你在主线程创建的一些Session 子线程没法使用,这种情况还得视系统而定。很明显,程序员还是要花费一定的心力来确保一些东西能够正确运作。我的意见是界面以及以及按键等事件的处理放到主线程处理,后台工作线程和主线程仅仅有数据的交互。也就是说,后台线程准备好数据之后,让主线程使用,主线程在接收到事件指示后发送命令给子线程。按照这样的逻辑就能够达到界面高响应的目标。