现在的位置: 首页 > 综合 > 正文

pb多线程的实现

2013年10月08日 ⁄ 综合 ⁄ 共 19244字 ⁄ 字号 评论关闭

最近在看.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 -----------

  1. //将w_ThreadTest窗口设为回调对象的父对象 *明义上
  2. QueryCallBack.of_SetParent(Parent)
  3. 新建6个NonVisualObject对象:
  4. nvo_ThreadBase //用于创建线程的基类对象.
  5. nvo_ExpThread //导出线程,继承自nvo_ThreadBase
  6. nvo_QueryThread //查询线程,继承自nvo_ThreadBase
  7. nvo_CallBackBase //线程的回调基类对象
  8. nvo_ExpCallBack //导出线程回调对象,继承自nvo_CallBack
  9. nvo_QueryCallBack //查询线程回调对象,继承自nvo_CallBack
  10. /*
  11. 用于主线程与子线程间数据交互.*一个回调对象可以为多个线程共享,但不建议这么做
  12. 因为可能会有一些不可预知的事情发生,我推荐一个线程对应一个回调对象,但回调对象可以有多个父对象
  13. */
  14. //----------nvo_CallBackBase-------//
  15. 添加以下实例变量:
  16. Private:
  17. window iw_Parent //回调用的父对象,使用window类型是因为这样可以使用多态编写通用代码
  18. boolean ib_ThreadExecuted //线程状态标志
  19. 添加以下函数:
  20. Public (none) of_SetParent(window aw_parent)
  21. iw_Parent = aw_parent
  22. Public boolean of_IsExecuted()
  23. return ib_ThreadExecuted
  24. Protected (none) of_Dispose()
  25. //释放资源
  26. 添加以下自定义事件:
  27. (none) ue_ThreadStart(string ThreadName)
  28. ib_ThreadExecuted = true
  29. if IsValid(iw_Parent) then iw_Parent.Dynamic Event(ThreadName,this)
  30. (none) ue_ThreadStop(string ThreadName)
  31. ib_ThreadExecuted = false
  32. if IsValid(iw_Parent) then iw_Parent.Dynamic Event(ThreadName,this)
  33. of_Dispose()
  34. (none) ue_ThreadError(string ThreadName,string ErrorText)
  35. if IsValid(iw_Parent) then iw_Parent.Dynamic Event(ThreadName,this,ErrorText)
  36. (none) ue_ThreadMsg(string ThreadName,string Msg)
  37. if IsValid(iw_Parent) then iw_Parent.Dynamic Event(ThreadName,this,Msg)
  38. //------------------------//
  39. //----------nvo_ExpCallBack(继承自nvo_CallBackBase)-------//
  40. /*
  41. 没有要添加的功能,纯粹为了更优雅.*以后可以扩展功能
  42. */
  43. //------------------------//
  44. //----------nvo_QueryCallBack(继承自nvo_CallBackBase)-------//
  45. 添加以下实例变量:
  46. Private:
  47. Datawindow idw_QueryTarget //回调填充对象
  48. 添加以下函数:
  49. Public (none) of_SetQueryTarget(datawindow adw_Target)
  50. idw_QueryTarget = adw_Target
  51. Public integer of_SetFullState(ref blob abb_data)
  52. int li_ret
  53. if Not IsValid(idw_QueryTarget) thenreturn -1
  54. idw_QueryTarget.SetRedraw(False)
  55. idw_QueryTarget.Event RetrieveStart()
  56. li_ret = idw_QueryTarget.SetFullState(ref abb_data)
    //填充数据
  57. idw_QueryTarget.Event RetrieveEnd(idw_QueryTarget.RowCount())
  58. idw_QueryTarget.SetRedraw(True)
  59. return li_ret
  60. /*
  61. 由于SetFullState会将目标DW的transobject也覆盖了,并且还会将DW的Select语句覆盖
  62. 所以填充之后要记得将其transobject和Select设回原来的值!
  63. */
  64. //------------------------//
  65. //----------nvo_Thread-------//
  66. 添加以下实例变量:
  67. Protected:
  68. string is_ThreadName="子线程"//线程名
  69. boolean ib_Executed //状态标志
  70. 添加以下函数:
  71. Public (none) of_SetThreadName(string as_name)
  72. is_ThreadName = as_name
  73. Protected (none) of_Task()
  74. //处理的任务
  75. Public (none) of_Start()
  76. //启动线程
  77. Public (none) of_Stop() //停止线程
  78. ib_Executed = false
  79. Protected (none) of_Dispose()
  80. //释放资源
  81. //------------------------//
  82. //----------nvo_ExpThread(继承自nvo_Thread)-------//
  83. 添加以下实例变量:
  84. Private:
  85. nvo_ExpCallBack CallBack //线程回调对象
  86. Datastore ids_Exp //导出数据用的临时存放的DS
  87. 添加以下函数:
  88. Public (none) of_SetCallBack(nvo_ExpCallBack an_CallBack)
  89. CallBack = an_CallBack
  90. Public (none) of_DataExp(ref blob abb_data)
  91. /*
  92. 因为子线程不能操作主线程的引用类型变量,所以只能将数据直接传过来.
  93. 这种拷贝Datawindow或Datastore数据的方法相对于如object.datawindow.Data
  94. 取字符串的方式要快4~5倍的样子(实测),而且因为这个用GetFullState()取出的二进制数据里
  95. 还包括了对象定义等信息,所以下面就不用定义ids_Exp的DataObject什么的了.
  96. */
  97. if Not IsValid(ids_Exp) then
  98. ids_Exp = Create Datastore
  99. end if
  100. ids_Exp.SetFullState(ref abb_data)
  101. 重写父对象的函数:
  102. Protected (none) of_Task() //重写父对象的of_Task
  103. if IsValid(Datastore) then
  104. Yield() //先执行完消息队列中的函数*就是执行外部后面Post进来的函数
  105. if Not ib_Executed then//如果被外部停止则退出函数,这样可以做线程取消功能
  106. return
  107. end if
  108. Datastore.SaveAs() //可以用OLE或其它方面处理导出的数据,这里只用SaveAs为演示
  109. else
  110. CallBack.Event ue_ThreadError(is_ThreadName,"Datastore未实例化!")
  111. return
  112. end if
  113. Public (none) of_Start() //启动线程
  114. if ib_Executed thenreturn
  115. if Not IsValid(CallBack) then
  116. /*
  117. 注释:2012-04-18
  118. 这在子线程创建Callback对象只是为了保证如果调用方未设置Callback,也能够正常工作
  119. 当然这里也可以直接return
  120. */
  121. CallBack = Create nvo_ExpCallBack
  122. end if
  123. ib_Executed = true
  124. CallBack.Event ue_Start(is_ThreadName)
  125. of_Task()
  126. ib_Executed = false
  127. CallBack.Event ue_Stop(is_ThreadName)
  128. of_Dispose()
  129. Protected (none) of_Dispose()
  130. Destroy ids_Exp
  131. //------------------------//
  132. //----------nvo_QueryThread(继承自nvo_Thread)-------//
  133. 添加以下实例变量:
  134. Private:
  135. nvo_QueryCallBack CallBack //线程回调对象
  136. string is_Dataobject //查询对象的Dataobject
  137. 添加以下函数:
  138. Public (none) of_SetCallBack(nvo_QueryCallBack an_CallBack)
  139. CallBack = an_CallBack
  140. Public (none) of_SetDataObject(string as_dataobject)
  141. is_Dataobject = as_dataobject
  142. /*
  143. 这里假设是单事务的情况下,否则你可能需要记录提供采用什么事务来进行数据查询
  144. 因为在子线程中是不能使用主线程中已有的事务的,比如SQLCA也不行.
  145. */
  146. 重写父对象的函数:
  147. Protected (none) of_Task() //重写父对象的of_Task
  148. if is_Dataobject<>"" then
  149. Yield() //先执行完消息队列中的函数*就是执行外部后面Post进来的函数
  150. if Not ib_Executed then
    //如果被外部停止则退出函数,这样可以做线程取消功能
  151. return
  152. end if
  153. Transaction lts_Trans
  154. Datastore lds_Query
  155. blob lbb_Data
  156. lts_Trans = Create Transaction
  157. /*设置事务信息*/
  158. Connect Using lts_Trans;
  159. lds_Query = Create Datastore
  160. Datastore.SetTransObject(lts_Trans)
  161. Datastore.Dataobject = is_Dataobject
  162. lds_Query.Retrieve()
  163. Yield()
  164. if Not ib_Executed then
  165. return
  166. end if
  167. lds_Query.GetFullState(ref lbb_Data)
  168. if CallBack.of_SetFullState(ref lbb_Data) = -1 then
  169. CallBack.Event ue_ThreadError(is_ThreadName,"填充数据失败!")
  170. end if
  171. Disconnect Using lts_Trans;
  172. Destroy lds_Query
  173. Destroy lts_Trans
  174. else
  175. CallBack.Event ue_ThreadError(is_ThreadName,"is_Dataobject为空!")
  176. return
  177. end if
  178. Public (none) of_Start() //启动线程
  179. if ib_Executed thenreturn
  180. if Not IsValid(CallBack) then
  181. /*
  182. 注释:2012-04-18
  183. 这在子线程创建Callback对象只是为了保证如果调用方未设置Callback,也能够正常工作
  184. 当然这里也可以直接return
  185. */
  186. CallBack = Create nvo_QueryCallBack
  187. end if
  188. ib_Executed = true
  189. CallBack.Event ue_Start(is_ThreadName)
  190. of_Task()
  191. ib_Executed = false
  192. CallBack.Event ue_Stop(is_ThreadName)
  193. of_Dispose()
  194. //------------------------//
  195. 新建1个Window窗口:
  196. w_ThreadTest //测试窗口
  197. //---------w_ThreadTest-------//
  198. 添加以下实例变量:
  199. nvo_ExpThread ExpThread //导出线程
  200. nvo_QueryThread QueryThread //查询线程
  201. nvo_ExpCallBack ExpCallBack //导出线程回调对象
  202. nvo_QueryCallBack QueryCallBack //查询线程回调对象
  203. 添加以下自定义事件:
  204. (none) ue_ThreadStart(string ThreadName,nvo_CallBackBase CallBack)
  205. /*
  206. 线程任务开始,在此添加响应代码
  207. ThreadName 线程名
  208. CallBack 回调对象
  209. */
  210. Messagebox("["+ThreadName+"]"+"提示","线程任务开始")
  211. (none) ue_ThreadStop(string ThreadName,nvo_CallBackBase CallBack)
  212. /*
  213. 线程任务结束,在此添加响应代码
  214. ThreadName 线程名
  215. CallBack 回调对象
  216. */
  217. Messagebox("["+ThreadName+"]"+"提示","线程任务结束")
  218. (none) ue_ThreadError(string ThreadName,nvo_CallBackBase CallBack,string ErrorText)
  219. /*
  220. 线程错误,在此添加响应代码
  221. ThreadName 线程名
  222. CallBack 回调对象
  223. ErrorText 错误信息
  224. */
  225. Messagebox("["+ThreadName+"]"+"错误",ErrorText,Stopsign!)
  226. (none) ue_ThreadMsg(string ThreadName,nvo_CallBackBase CallBack,string Msg)
  227. /*
  228. 线程发来的信息,在此添加响应代码
  229. ThreadName 线程名
  230. CallBack 回调对象
  231. Msg 信息
  232. */
  233. Messagebox("["+ThreadName+"]"+"消息","Msg")
  234. 在以下窗口Close事件中添加代码:
  235. if IsValid(ExpThread) then
  236. SharedObjectUnRegister("ExpThreadInstance")
  237. Destroy ExpThread
  238. Destroy ExpCallBack
  239. end if
  240. if IsValid(QueryThread) then
  241. SharedObjectUnRegister("QueryThreadInstance")
  242. Destroy QueryThread
  243. Destroy QueryCallBack
  244. end if
  245. 添加一个数据窗口dw_Test
  246. 添加一个按钮cb_QueryThread,在其Clicked事件下写:
  247. if Not IsValid(QueryThread) then
  248. //用nvo_QueryThread类型注册一个查询线程实例,名为QueryThreadInstance
  249. if SharedObjectRegister("nvo_QueryThread","QueryThreadInstance") <> Success! then
  250. Messagebox("错误","创建线程失败!",Stopsign!)
  251. return
  252. end if
  253. //获取一个名为QueryThreadInstance的线程实例引用,并赋给QueryThread *QueryThread的类型一定要是QueryThreadInstance注册时的类型是继承自那个类型
  254. if SharedObjectGet("QueryThreadInstance",QueryThread) <> Success! then
  255. Messagebox("错误","获取线程失败!",Stopsign!)
  256. return
  257. end if
  258. QueryThread.of_SetThreadName("查询线程")
  259. end if
  260. if Not IsValid(QueryCallBack) then
  261. //创建查询回调对象实例
  262. QueryCallBack = Create nvo_QueryCallBack
  263. //将w_ThreadTest窗口设为回调对象的父对象 *明义上
  264. QueryCallBack.of_SetParent(Parent)
  265. //添加用于查询线程返回数据填充的DW窗口
  266. QueryCallBack.of_SetQueryTarget(dw_Test)
  267. /* 以下代码于2012-04-12纠正 */
  268. QueryThread.of_SetCallback(QueryCallBack)
  269. end if
  270. if QueryCallBack.of_IsExecuted() then
  271. /*
  272. 如果查询线程正在执行则停止它
  273. 此时只能用Post向线程发送消息,如果直接使用QueryThread.of_Stop()将会造成线程死锁
  274. */
  275. QueryThread.Post of_Stop() //停止线程任务
  276. return
  277. end if
  278. //因为此时查询线程未在执行任务,所以可以用同步的方式调用其上的函数
  279. QueryThread.of_SetDataObject(dw_Test.Dataobject)
  280. //此时用Post将消息添加到查询线程的消息队列上,并将控制权交会主线程,做到异步执行,也就是让查询线程在后台执行of_Start()函数
  281. QueryThread.Post of_Start()
  282. 添加一个按钮cb_ExpThread,在其Clicked事件下写:
  283. blob lbb_data
  284. if Not IsValid(ExpThread) then
  285. if SharedObjectRegister("nvo_ExpThread","ExpThreadInstance") <> Success! then
  286. Messagebox("错误","创建线程失败!",Stopsign!)
  287. return
  288. end if
  289. if SharedObjectGet("ExpThreadInstance",ExpThread) <> Success! then
  290. Messagebox("错误","获取线程失败!",Stopsign!)
  291. return
  292. end if
  293. ExpThread.of_SetThreadName("导出线程")
  294. end if
  295. if Not IsValid(ExpCallBack) then
  296. ExpCallBack = Create nvo_ExpCallBack
  297. ExpCallBack.of_SetParent(Parent)
  298. /* 以下代码于2012-04-12纠正 */
  299. ExpThread.of_SetCallback(ExpCallBack)
  300. end if
  301. if ExpCallBack.of_IsExecuted() then
  302. ExpThread.Post of_Stop() //停止线程任务
  303. return
  304. end if
  305. //将DW中的数据以二进制形式导出,放在lbb_data中
  306. dw_Test.GetFullState(ref lbb_data)
  307. //将lbb_data传给导出线程
  308. ExpThread.of_DataExp(ref lbb_data)
  309. //开始线程
  310. ExpThread.Post of_Start()
  311. //------------------------//

------------- 结束 -----------
上面的代码全部纯手敲,未经过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

抱歉!评论已关闭.