最近在看.Net多线程的时候突然想到PowerBuiler是否支持多线程开发呢?于是Google了下,原来真的可以,不过比较遗憾的是这关于PB多线程方面的资料无论是英文的还是中文的都十分稀缺,完全没有详细的资料可查,连官网都一样,PB自带的帮助文档就更不用说的,简陋啊!!于是自己摸着石头过河,其中也遇到不少莫名其妙的问题,但多数是IDE本身语义检测的缺陷,谁让这不是VS呢?...
----------- 正题分割线 -----------
在PB里要使用多线程开发应用程序必须的3个系统函数:
SharedObjectRegister(string classname,string instancename):
函数功能:共享一个以classname为类型名称的对象,并产生一个新的线程实例,名为instancename.
返回值:枚举类型,Success!表示成功,其他的可以F1.
SharedObjectGet(string instancename,objectinstance):
函数功能:获取SharedObjectRegister创建的线程对象实例的引用,并赋值给objectinstance变量.*objectinstance类型是instancename注册时的类型或继承自它.如果获取不成功,将不对objectinstance操作.
返回值:同上.
SharedObjectUnRegister(string instancename):
函数功能:注销线程实例名为instancename的子线程.
返回值:同上.
还有一个函数SharedObjectDirectory(string instancenames[]{,string classnames[]})函数,得到所有注册的实例名和注册的类型.
PB的多线程有很多限止,以下几个目前发现需要注意的地方:
1.主线程与子线程的资源是相互独立的,子线程可以访问在程序中定义的全局变量,但在线程中对其做的任何修改将不对主线程产生影响.如果有一个全局的引用类型的变量,虽然在主线程中已经实例化但在子线程中访问的时候它是没有被实例化的,这就是它们资源相互独立的地方,主线程与子线程都有相同的全局变量定义,但其作用域只在它们各自的线程内,子线程与子线程之间也是一样,就算是同一个对象里定义了Shared Variables,注册为不同的线程实例,它的资源也是相互独立的,这一点非常的重要.
2.主线程与子线程通讯只能通过NonVisualObject类型或继承自NonVisualObject类型的实例来传递数据,而且传递的数据只能是基本类型的数据,不能传递引用类型的数据,如datastore,transaction等.所有的通讯都只能在这个NonVisualObject类型的中间对象处理,NonVisualObject类型是运行在主线程的.引用类型在哪个线程里Create就是哪一个线程中的资源.
3.操作子线程时,如果是异步调用则必须在函数调用前加Post关键字,如果不加则是同步调用.
4.如果子线程正在执行函数,那么此时不能同步对子线程进行操作,否则将会使线程阻塞造成死锁,所以一定要处理好线程间的资源同步与互斥,这就是为什么有很多人抱怨PB多线程容易使程序挂掉的关键所在.
5.使用SharedObjectUnRegister将线程注销后不会销毁注册的对象实例,而那个对象实例仍然是工作在子线程上过的,不过不能再用SharedObjectGet获取到该线程名的实例引用,但此时可以以这个名字重新注册它,所以当你不再使子线程时应该及时销毁其实例对象以回收内存.
6.子线程Halt了主线程不会退出,相反主线程Halt子线程会退出.
7.SharedObjectRegister注册后的线程可以在程序的任何位置获取,线程名必须是唯一的.
----------- Sample code -----------
- //将w_ThreadTest窗口设为回调对象的父对象 *明义上
- QueryCallBack.of_SetParent(Parent)
- 新建6个NonVisualObject对象:
- nvo_ThreadBase //用于创建线程的基类对象.
- nvo_ExpThread //导出线程,继承自nvo_ThreadBase
- nvo_QueryThread //查询线程,继承自nvo_ThreadBase
- nvo_CallBackBase //线程的回调基类对象
- nvo_ExpCallBack //导出线程回调对象,继承自nvo_CallBack
- nvo_QueryCallBack //查询线程回调对象,继承自nvo_CallBack
- /*
- 用于主线程与子线程间数据交互.*一个回调对象可以为多个线程共享,但不建议这么做
- 因为可能会有一些不可预知的事情发生,我推荐一个线程对应一个回调对象,但回调对象可以有多个父对象
- */
- //----------nvo_CallBackBase-------//
- 添加以下实例变量:
- Private:
- window iw_Parent //回调用的父对象,使用window类型是因为这样可以使用多态编写通用代码
- boolean ib_ThreadExecuted //线程状态标志
- 添加以下函数:
- Public (none) of_SetParent(window aw_parent)
- iw_Parent = aw_parent
- Public boolean of_IsExecuted()
- return ib_ThreadExecuted
- Protected (none) of_Dispose()
- //释放资源
- 添加以下自定义事件:
- (none) ue_ThreadStart(string ThreadName)
- ib_ThreadExecuted = true
- if IsValid(iw_Parent) then iw_Parent.Dynamic Event(ThreadName,this)
- (none) ue_ThreadStop(string ThreadName)
- ib_ThreadExecuted = false
- if IsValid(iw_Parent) then iw_Parent.Dynamic Event(ThreadName,this)
- of_Dispose()
- (none) ue_ThreadError(string ThreadName,string ErrorText)
- if IsValid(iw_Parent) then iw_Parent.Dynamic Event(ThreadName,this,ErrorText)
- (none) ue_ThreadMsg(string ThreadName,string Msg)
- if IsValid(iw_Parent) then iw_Parent.Dynamic Event(ThreadName,this,Msg)
- //------------------------//
- //----------nvo_ExpCallBack(继承自nvo_CallBackBase)-------//
- /*
- 没有要添加的功能,纯粹为了更优雅.*以后可以扩展功能
- */
- //------------------------//
- //----------nvo_QueryCallBack(继承自nvo_CallBackBase)-------//
- 添加以下实例变量:
- Private:
- Datawindow idw_QueryTarget //回调填充对象
- 添加以下函数:
- Public (none) of_SetQueryTarget(datawindow adw_Target)
- idw_QueryTarget = adw_Target
- Public integer of_SetFullState(ref blob abb_data)
- int li_ret
- if Not IsValid(idw_QueryTarget) thenreturn -1
- idw_QueryTarget.SetRedraw(False)
- idw_QueryTarget.Event RetrieveStart()
- li_ret = idw_QueryTarget.SetFullState(ref abb_data)
//填充数据 - idw_QueryTarget.Event RetrieveEnd(idw_QueryTarget.RowCount())
- idw_QueryTarget.SetRedraw(True)
- return li_ret
- /*
- 由于SetFullState会将目标DW的transobject也覆盖了,并且还会将DW的Select语句覆盖
- 所以填充之后要记得将其transobject和Select设回原来的值!
- */
- //------------------------//
- //----------nvo_Thread-------//
- 添加以下实例变量:
- Protected:
- string is_ThreadName="子线程"//线程名
- boolean ib_Executed //状态标志
- 添加以下函数:
- Public (none) of_SetThreadName(string as_name)
- is_ThreadName = as_name
- Protected (none) of_Task()
- //处理的任务
- Public (none) of_Start()
- //启动线程
- Public (none) of_Stop() //停止线程
- ib_Executed = false
- Protected (none) of_Dispose()
- //释放资源
- //------------------------//
- //----------nvo_ExpThread(继承自nvo_Thread)-------//
- 添加以下实例变量:
- Private:
- nvo_ExpCallBack CallBack //线程回调对象
- Datastore ids_Exp //导出数据用的临时存放的DS
- 添加以下函数:
- Public (none) of_SetCallBack(nvo_ExpCallBack an_CallBack)
- CallBack = an_CallBack
- Public (none) of_DataExp(ref blob abb_data)
- /*
- 因为子线程不能操作主线程的引用类型变量,所以只能将数据直接传过来.
- 这种拷贝Datawindow或Datastore数据的方法相对于如object.datawindow.Data
- 取字符串的方式要快4~5倍的样子(实测),而且因为这个用GetFullState()取出的二进制数据里
- 还包括了对象定义等信息,所以下面就不用定义ids_Exp的DataObject什么的了.
- */
- if Not IsValid(ids_Exp) then
- ids_Exp = Create Datastore
- end if
- ids_Exp.SetFullState(ref abb_data)
- 重写父对象的函数:
- Protected (none) of_Task() //重写父对象的of_Task
- if IsValid(Datastore) then
- Yield() //先执行完消息队列中的函数*就是执行外部后面Post进来的函数
- if Not ib_Executed then//如果被外部停止则退出函数,这样可以做线程取消功能
- return
- end if
- Datastore.SaveAs() //可以用OLE或其它方面处理导出的数据,这里只用SaveAs为演示
- else
- CallBack.Event ue_ThreadError(is_ThreadName,"Datastore未实例化!")
- return
- end if
- Public (none) of_Start() //启动线程
- if ib_Executed thenreturn
- if Not IsValid(CallBack) then
- /*
- 注释:2012-04-18
- 这在子线程创建Callback对象只是为了保证如果调用方未设置Callback,也能够正常工作
- 当然这里也可以直接return
- */
- CallBack = Create nvo_ExpCallBack
- end if
- ib_Executed = true
- CallBack.Event ue_Start(is_ThreadName)
- of_Task()
- ib_Executed = false
- CallBack.Event ue_Stop(is_ThreadName)
- of_Dispose()
- Protected (none) of_Dispose()
- Destroy ids_Exp
- //------------------------//
- //----------nvo_QueryThread(继承自nvo_Thread)-------//
- 添加以下实例变量:
- Private:
- nvo_QueryCallBack CallBack //线程回调对象
- string is_Dataobject //查询对象的Dataobject
- 添加以下函数:
- Public (none) of_SetCallBack(nvo_QueryCallBack an_CallBack)
- CallBack = an_CallBack
- Public (none) of_SetDataObject(string as_dataobject)
- is_Dataobject = as_dataobject
- /*
- 这里假设是单事务的情况下,否则你可能需要记录提供采用什么事务来进行数据查询
- 因为在子线程中是不能使用主线程中已有的事务的,比如SQLCA也不行.
- */
- 重写父对象的函数:
- Protected (none) of_Task() //重写父对象的of_Task
- if is_Dataobject<>"" then
- Yield() //先执行完消息队列中的函数*就是执行外部后面Post进来的函数
- if Not ib_Executed then
//如果被外部停止则退出函数,这样可以做线程取消功能 - return
- end if
- Transaction lts_Trans
- Datastore lds_Query
- blob lbb_Data
- lts_Trans = Create Transaction
- /*设置事务信息*/
- Connect Using lts_Trans;
- lds_Query = Create Datastore
- Datastore.SetTransObject(lts_Trans)
- Datastore.Dataobject = is_Dataobject
- lds_Query.Retrieve()
- Yield()
- if Not ib_Executed then
- return
- end if
- lds_Query.GetFullState(ref lbb_Data)
- if CallBack.of_SetFullState(ref lbb_Data) = -1 then
- CallBack.Event ue_ThreadError(is_ThreadName,"填充数据失败!")
- end if
- Disconnect Using lts_Trans;
- Destroy lds_Query
- Destroy lts_Trans
- else
- CallBack.Event ue_ThreadError(is_ThreadName,"is_Dataobject为空!")
- return
- end if
- Public (none) of_Start() //启动线程
- if ib_Executed thenreturn
- if Not IsValid(CallBack) then
- /*
- 注释:2012-04-18
- 这在子线程创建Callback对象只是为了保证如果调用方未设置Callback,也能够正常工作
- 当然这里也可以直接return
- */
- CallBack = Create nvo_QueryCallBack
- end if
- ib_Executed = true
- CallBack.Event ue_Start(is_ThreadName)
- of_Task()
- ib_Executed = false
- CallBack.Event ue_Stop(is_ThreadName)
- of_Dispose()
- //------------------------//
- 新建1个Window窗口:
- w_ThreadTest //测试窗口
- //---------w_ThreadTest-------//
- 添加以下实例变量:
- nvo_ExpThread ExpThread //导出线程
- nvo_QueryThread QueryThread //查询线程
- nvo_ExpCallBack ExpCallBack //导出线程回调对象
- nvo_QueryCallBack QueryCallBack //查询线程回调对象
- 添加以下自定义事件:
- (none) ue_ThreadStart(string ThreadName,nvo_CallBackBase CallBack)
- /*
- 线程任务开始,在此添加响应代码
- ThreadName 线程名
- CallBack 回调对象
- */
- Messagebox("["+ThreadName+"]"+"提示","线程任务开始")
- (none) ue_ThreadStop(string ThreadName,nvo_CallBackBase CallBack)
- /*
- 线程任务结束,在此添加响应代码
- ThreadName 线程名
- CallBack 回调对象
- */
- Messagebox("["+ThreadName+"]"+"提示","线程任务结束")
- (none) ue_ThreadError(string ThreadName,nvo_CallBackBase CallBack,string ErrorText)
- /*
- 线程错误,在此添加响应代码
- ThreadName 线程名
- CallBack 回调对象
- ErrorText 错误信息
- */
- Messagebox("["+ThreadName+"]"+"错误",ErrorText,Stopsign!)
- (none) ue_ThreadMsg(string ThreadName,nvo_CallBackBase CallBack,string Msg)
- /*
- 线程发来的信息,在此添加响应代码
- ThreadName 线程名
- CallBack 回调对象
- Msg 信息
- */
- Messagebox("["+ThreadName+"]"+"消息","Msg")
- 在以下窗口Close事件中添加代码:
- if IsValid(ExpThread) then
- SharedObjectUnRegister("ExpThreadInstance")
- Destroy ExpThread
- Destroy ExpCallBack
- end if
- if IsValid(QueryThread) then
- SharedObjectUnRegister("QueryThreadInstance")
- Destroy QueryThread
- Destroy QueryCallBack
- end if
- 添加一个数据窗口dw_Test
- 添加一个按钮cb_QueryThread,在其Clicked事件下写:
- if Not IsValid(QueryThread) then
- //用nvo_QueryThread类型注册一个查询线程实例,名为QueryThreadInstance
- if SharedObjectRegister("nvo_QueryThread","QueryThreadInstance") <> Success! then
- Messagebox("错误","创建线程失败!",Stopsign!)
- return
- end if
- //获取一个名为QueryThreadInstance的线程实例引用,并赋给QueryThread *QueryThread的类型一定要是QueryThreadInstance注册时的类型是继承自那个类型
- if SharedObjectGet("QueryThreadInstance",QueryThread) <> Success! then
- Messagebox("错误","获取线程失败!",Stopsign!)
- return
- end if
- QueryThread.of_SetThreadName("查询线程")
- end if
- if Not IsValid(QueryCallBack) then
- //创建查询回调对象实例
- QueryCallBack = Create nvo_QueryCallBack
- //将w_ThreadTest窗口设为回调对象的父对象 *明义上
- QueryCallBack.of_SetParent(Parent)
- //添加用于查询线程返回数据填充的DW窗口
- QueryCallBack.of_SetQueryTarget(dw_Test)
- /* 以下代码于2012-04-12纠正 */
- QueryThread.of_SetCallback(QueryCallBack)
- end if
- if QueryCallBack.of_IsExecuted() then
- /*
- 如果查询线程正在执行则停止它
- 此时只能用Post向线程发送消息,如果直接使用QueryThread.of_Stop()将会造成线程死锁
- */
- QueryThread.Post of_Stop() //停止线程任务
- return
- end if
- //因为此时查询线程未在执行任务,所以可以用同步的方式调用其上的函数
- QueryThread.of_SetDataObject(dw_Test.Dataobject)
- //此时用Post将消息添加到查询线程的消息队列上,并将控制权交会主线程,做到异步执行,也就是让查询线程在后台执行of_Start()函数
- QueryThread.Post of_Start()
- 添加一个按钮cb_ExpThread,在其Clicked事件下写:
- blob lbb_data
- if Not IsValid(ExpThread) then
- if SharedObjectRegister("nvo_ExpThread","ExpThreadInstance") <> Success! then
- Messagebox("错误","创建线程失败!",Stopsign!)
- return
- end if
- if SharedObjectGet("ExpThreadInstance",ExpThread) <> Success! then
- Messagebox("错误","获取线程失败!",Stopsign!)
- return
- end if
- ExpThread.of_SetThreadName("导出线程")
- end if
- if Not IsValid(ExpCallBack) then
- ExpCallBack = Create nvo_ExpCallBack
- ExpCallBack.of_SetParent(Parent)
- /* 以下代码于2012-04-12纠正 */
- ExpThread.of_SetCallback(ExpCallBack)
- end if
- if ExpCallBack.of_IsExecuted() then
- ExpThread.Post of_Stop() //停止线程任务
- return
- end if
- //将DW中的数据以二进制形式导出,放在lbb_data中
- dw_Test.GetFullState(ref lbb_data)
- //将lbb_data传给导出线程
- ExpThread.of_DataExp(ref lbb_data)
- //开始线程
- ExpThread.Post of_Start()
- //------------------------//
//将w_ThreadTest窗口设为回调对象的父对象 *明义上 QueryCallBack.of_SetParent(Parent) 新建6个NonVisualObject对象: nvo_ThreadBase //用于创建线程的基类对象. nvo_ExpThread //导出线程,继承自nvo_ThreadBase nvo_QueryThread //查询线程,继承自nvo_ThreadBase nvo_CallBackBase //线程的回调基类对象 nvo_ExpCallBack //导出线程回调对象,继承自nvo_CallBack nvo_QueryCallBack //查询线程回调对象,继承自nvo_CallBack /* 用于主线程与子线程间数据交互.*一个回调对象可以为多个线程共享,但不建议这么做 因为可能会有一些不可预知的事情发生,我推荐一个线程对应一个回调对象,但回调对象可以有多个父对象 */ //----------nvo_CallBackBase-------// 添加以下实例变量: Private: window iw_Parent //回调用的父对象,使用window类型是因为这样可以使用多态编写通用代码 boolean ib_ThreadExecuted //线程状态标志 添加以下函数: Public (none) of_SetParent(window aw_parent) iw_Parent = aw_parent Public boolean of_IsExecuted() return ib_ThreadExecuted Protected (none) of_Dispose() //释放资源 添加以下自定义事件: (none) ue_ThreadStart(string ThreadName) ib_ThreadExecuted = true if IsValid(iw_Parent) then iw_Parent.Dynamic Event(ThreadName,this) (none) ue_ThreadStop(string ThreadName) ib_ThreadExecuted = false if IsValid(iw_Parent) then iw_Parent.Dynamic Event(ThreadName,this) of_Dispose() (none) ue_ThreadError(string ThreadName,string ErrorText) if IsValid(iw_Parent) then iw_Parent.Dynamic Event(ThreadName,this,ErrorText) (none) ue_ThreadMsg(string ThreadName,string Msg) if IsValid(iw_Parent) then iw_Parent.Dynamic Event(ThreadName,this,Msg) //------------------------// //----------nvo_ExpCallBack(继承自nvo_CallBackBase)-------// /* 没有要添加的功能,纯粹为了更优雅.*以后可以扩展功能 */ //------------------------// //----------nvo_QueryCallBack(继承自nvo_CallBackBase)-------// 添加以下实例变量: Private: Datawindow idw_QueryTarget //回调填充对象 添加以下函数: Public (none) of_SetQueryTarget(datawindow adw_Target) idw_QueryTarget = adw_Target Public integer of_SetFullState(ref blob abb_data) int li_ret if Not IsValid(idw_QueryTarget) then return -1 idw_QueryTarget.SetRedraw(False) idw_QueryTarget.Event RetrieveStart() li_ret = idw_QueryTarget.SetFullState(ref abb_data) //填充数据 idw_QueryTarget.Event RetrieveEnd(idw_QueryTarget.RowCount()) idw_QueryTarget.SetRedraw(True) return li_ret /* 由于SetFullState会将目标DW的transobject也覆盖了,并且还会将DW的Select语句覆盖 所以填充之后要记得将其transobject和Select设回原来的值! */ //------------------------// //----------nvo_Thread-------// 添加以下实例变量: Protected: string is_ThreadName="子线程" //线程名 boolean ib_Executed //状态标志 添加以下函数: Public (none) of_SetThreadName(string as_name) is_ThreadName = as_name Protected (none) of_Task() //处理的任务 Public (none) of_Start() //启动线程 Public (none) of_Stop() //停止线程 ib_Executed = false Protected (none) of_Dispose() //释放资源 //------------------------// //----------nvo_ExpThread(继承自nvo_Thread)-------// 添加以下实例变量: Private: nvo_ExpCallBack CallBack //线程回调对象 Datastore ids_Exp //导出数据用的临时存放的DS 添加以下函数: Public (none) of_SetCallBack(nvo_ExpCallBack an_CallBack) CallBack = an_CallBack Public (none) of_DataExp(ref blob abb_data) /* 因为子线程不能操作主线程的引用类型变量,所以只能将数据直接传过来. 这种拷贝Datawindow或Datastore数据的方法相对于如object.datawindow.Data 取字符串的方式要快4~5倍的样子(实测),而且因为这个用GetFullState()取出的二进制数据里 还包括了对象定义等信息,所以下面就不用定义ids_Exp的DataObject什么的了. */ if Not IsValid(ids_Exp) then ids_Exp = Create Datastore end if ids_Exp.SetFullState(ref abb_data) 重写父对象的函数: Protected (none) of_Task() //重写父对象的of_Task if IsValid(Datastore) then Yield() //先执行完消息队列中的函数*就是执行外部后面Post进来的函数 if Not ib_Executed then //如果被外部停止则退出函数,这样可以做线程取消功能 return end if Datastore.SaveAs() //可以用OLE或其它方面处理导出的数据,这里只用SaveAs为演示 else CallBack.Event ue_ThreadError(is_ThreadName,"Datastore未实例化!") return end if Public (none) of_Start() //启动线程 if ib_Executed then return if Not IsValid(CallBack) then /* 注释:2012-04-18 这在子线程创建Callback对象只是为了保证如果调用方未设置Callback,也能够正常工作 当然这里也可以直接return */ CallBack = Create nvo_ExpCallBack end if ib_Executed = true CallBack.Event ue_Start(is_ThreadName) of_Task() ib_Executed = false CallBack.Event ue_Stop(is_ThreadName) of_Dispose() Protected (none) of_Dispose() Destroy ids_Exp //------------------------// //----------nvo_QueryThread(继承自nvo_Thread)-------// 添加以下实例变量: Private: nvo_QueryCallBack CallBack //线程回调对象 string is_Dataobject //查询对象的Dataobject 添加以下函数: Public (none) of_SetCallBack(nvo_QueryCallBack an_CallBack) CallBack = an_CallBack Public (none) of_SetDataObject(string as_dataobject) is_Dataobject = as_dataobject /* 这里假设是单事务的情况下,否则你可能需要记录提供采用什么事务来进行数据查询 因为在子线程中是不能使用主线程中已有的事务的,比如SQLCA也不行. */ 重写父对象的函数: Protected (none) of_Task() //重写父对象的of_Task if is_Dataobject<>"" then Yield() //先执行完消息队列中的函数*就是执行外部后面Post进来的函数 if Not ib_Executed then //如果被外部停止则退出函数,这样可以做线程取消功能 return end if Transaction lts_Trans Datastore lds_Query blob lbb_Data lts_Trans = Create Transaction /*设置事务信息*/ Connect Using lts_Trans; lds_Query = Create Datastore Datastore.SetTransObject(lts_Trans) Datastore.Dataobject = is_Dataobject lds_Query.Retrieve() Yield() if Not ib_Executed then return end if lds_Query.GetFullState(ref lbb_Data) if CallBack.of_SetFullState(ref lbb_Data) = -1 then CallBack.Event ue_ThreadError(is_ThreadName,"填充数据失败!") end if Disconnect Using lts_Trans; Destroy lds_Query Destroy lts_Trans else CallBack.Event ue_ThreadError(is_ThreadName,"is_Dataobject为空!") return end if Public (none) of_Start() //启动线程 if ib_Executed then return if Not IsValid(CallBack) then /* 注释:2012-04-18 这在子线程创建Callback对象只是为了保证如果调用方未设置Callback,也能够正常工作 当然这里也可以直接return */ CallBack = Create nvo_QueryCallBack end if ib_Executed = true CallBack.Event ue_Start(is_ThreadName) of_Task() ib_Executed = false CallBack.Event ue_Stop(is_ThreadName) of_Dispose() //------------------------// 新建1个Window窗口: w_ThreadTest //测试窗口 //---------w_ThreadTest-------// 添加以下实例变量: nvo_ExpThread ExpThread //导出线程 nvo_QueryThread QueryThread //查询线程 nvo_ExpCallBack ExpCallBack //导出线程回调对象 nvo_QueryCallBack QueryCallBack //查询线程回调对象 添加以下自定义事件: (none) ue_ThreadStart(string ThreadName,nvo_CallBackBase CallBack) /* 线程任务开始,在此添加响应代码 ThreadName 线程名 CallBack 回调对象 */ Messagebox("["+ThreadName+"]"+"提示","线程任务开始") (none) ue_ThreadStop(string ThreadName,nvo_CallBackBase CallBack) /* 线程任务结束,在此添加响应代码 ThreadName 线程名 CallBack 回调对象 */ Messagebox("["+ThreadName+"]"+"提示","线程任务结束") (none) ue_ThreadError(string ThreadName,nvo_CallBackBase CallBack,string ErrorText) /* 线程错误,在此添加响应代码 ThreadName 线程名 CallBack 回调对象 ErrorText 错误信息 */ Messagebox("["+ThreadName+"]"+"错误",ErrorText,Stopsign!) (none) ue_ThreadMsg(string ThreadName,nvo_CallBackBase CallBack,string Msg) /* 线程发来的信息,在此添加响应代码 ThreadName 线程名 CallBack 回调对象 Msg 信息 */ Messagebox("["+ThreadName+"]"+"消息","Msg") 在以下窗口Close事件中添加代码: if IsValid(ExpThread) then SharedObjectUnRegister("ExpThreadInstance") Destroy ExpThread Destroy ExpCallBack end if if IsValid(QueryThread) then SharedObjectUnRegister("QueryThreadInstance") Destroy QueryThread Destroy QueryCallBack end if 添加一个数据窗口dw_Test 添加一个按钮cb_QueryThread,在其Clicked事件下写: if Not IsValid(QueryThread) then //用nvo_QueryThread类型注册一个查询线程实例,名为QueryThreadInstance if SharedObjectRegister("nvo_QueryThread","QueryThreadInstance") <> Success! then Messagebox("错误","创建线程失败!",Stopsign!) return end if //获取一个名为QueryThreadInstance的线程实例引用,并赋给QueryThread *QueryThread的类型一定要是QueryThreadInstance注册时的类型是继承自那个类型 if SharedObjectGet("QueryThreadInstance",QueryThread) <> Success! then Messagebox("错误","获取线程失败!",Stopsign!) return end if QueryThread.of_SetThreadName("查询线程") end if if Not IsValid(QueryCallBack) then //创建查询回调对象实例 QueryCallBack = Create nvo_QueryCallBack //将w_ThreadTest窗口设为回调对象的父对象 *明义上 QueryCallBack.of_SetParent(Parent) //添加用于查询线程返回数据填充的DW窗口 QueryCallBack.of_SetQueryTarget(dw_Test) /* 以下代码于2012-04-12纠正 */ QueryThread.of_SetCallback(QueryCallBack) end if if QueryCallBack.of_IsExecuted() then /* 如果查询线程正在执行则停止它 此时只能用Post向线程发送消息,如果直接使用QueryThread.of_Stop()将会造成线程死锁 */ QueryThread.Post of_Stop() //停止线程任务 return end if //因为此时查询线程未在执行任务,所以可以用同步的方式调用其上的函数 QueryThread.of_SetDataObject(dw_Test.Dataobject) //此时用Post将消息添加到查询线程的消息队列上,并将控制权交会主线程,做到异步执行,也就是让查询线程在后台执行of_Start()函数 QueryThread.Post of_Start() 添加一个按钮cb_ExpThread,在其Clicked事件下写: blob lbb_data if Not IsValid(ExpThread) then if SharedObjectRegister("nvo_ExpThread","ExpThreadInstance") <> Success! then Messagebox("错误","创建线程失败!",Stopsign!) return end if if SharedObjectGet("ExpThreadInstance",ExpThread) <> Success! then Messagebox("错误","获取线程失败!",Stopsign!) return end if ExpThread.of_SetThreadName("导出线程") end if if Not IsValid(ExpCallBack) then ExpCallBack = Create nvo_ExpCallBack ExpCallBack.of_SetParent(Parent) /* 以下代码于2012-04-12纠正 */ ExpThread.of_SetCallback(ExpCallBack) end if if ExpCallBack.of_IsExecuted() then ExpThread.Post of_Stop() //停止线程任务 return end if //将DW中的数据以二进制形式导出,放在lbb_data中 dw_Test.GetFullState(ref lbb_data) //将lbb_data传给导出线程 ExpThread.of_DataExp(ref lbb_data) //开始线程 ExpThread.Post of_Start() //------------------------//
------------- 结束 -----------
上面的代码全部纯手敲,未经过PB环境调试,不过应该没什么问题,除非哪里打漏了.这里只是提供一个思路,可以在此基础上增加增量查询,过滤查询,多表查询,多表导出什么的,都是完全可以的!
其实PB的多线程功能对于数据库开发来说已经基本够用了,只要适当的变通一下.
PB的多线程用到的就几个函数
SharedObjectRegister()
SharedObjectGet()
SharedObjectUnregister()
用SharedObjectRegister(Classname,Instancename) PB帮助文件是Classname和Instancename,即类名和实例名。
用SharedObjectGet(instancename , objectinstance) 将实例名和具体的对象实例绑定,然后就可以通过对象实例POST 出发类里预定义的过程或函数。比如在类中预定义了uo_add函数,可以objectinstance.post uo_add
用SharedObjectUnregister(instancename) Unregisters a user object that was previously registered. 注销掉用户实例对象
eg.
1、定义一个nvo_multithread
添加add(int ai_n)函数
int li_i
int li_result = 0
sleep(5)
For li_i= 0 To ai_n
li_result+=li_i
Next
messagebox("",string(li_result))
2、定义一个窗口,在Instance Variables定义变量:nvo_multithread invo_thread
3、在Open事件中的代码:
invo_thread = Create nvo_multithread
SharedObjectRegister ("nvo_multithread" ,"thread1" )
SharedObjectGet ("thread1" , invo_thread)
4、在Close事件中的代码:
Destroy invo_thread
SharedObjectUnregister("thread1")
5、可以在一个按钮的Click事件中调用:
int i
invo_thread.post add(i)
调用后,就可以异步执行了
注意点:如果想要多个线程一起执行,就一定要多注册几个实例对象。即多执行几次2、4步。可以用数组来做。
比如我在Instance Variables定义成nvo_multithread invo_thead[5]
Open事件:For li_ii= 1 to 5
invo_th[li_ii] = Create nvo_multithread
SharedObjectRegister( "nvo_multithread" , "tthread" + string(li_ii) )
SharedObjectGet ("tthread" + string(li_ii) , invo_th[li_ii] )
Next
Close事件:For li_ii= 1 to 5
Destroy invo_th[li_ii]
SharedObjectUnregister("thread" + string(li_ii))
Next