注册 登录
编程论坛 VFP论坛

NetMTVFP(1.02版)

iswith 发布于 2022-11-19 13:47, 3502 次点击
*多线程调用实例V1.0(共享)
*20221116
*1.只能传一个参数,不能传送VFP对象,因为他都是字符串([参数1,参数2,参数3...]
*2.线程为单线程单元(STA)模式
*3.线程未完成VFP应该是不能退出(多测几次看会不会崩溃)

*这个实例功能还是很弱鸡,很多功能占时没有开放出来,等我测试好再说,
*最起码要得到线程的结果,一般这个结果可能是一个cursor,或返回值(这些暂时都没有)
*暂时你可以用一个本地的Cursor来共享多线动作后的数据。


*20221117V1.01( 收费版: 398 )
*!*    此时线程尚未处于运行状态。调用Start()函数启动线程,当前线程继续执行。
*!*    调用Join()函数可以阻塞当前线程,直到调用Join()的线程终止。
*!*    Thread类创建的线程默认为前台线程,可以通过IsBackground属性设置其为前台或后台线程。
*!*    还可以通过Priority属性设置线程的优先级。
*!*    如需中止线程,调用Abort()方法,在调用该方法的线程上抛出ThreadAbortException异常,
*!*    以结束该线程。线程内部可以通过try catch捕获该异常,在catch模块中进行一些必要的处理,
*!*    如释放持有的锁和文件资源等,
*!*    还可以通过Thread.ResetAbort()方法阻止线程的中止。
*!*    但是通常来说,应当慎重使用Abort()方法,如果在当前线程中抛出该异常,
*!*    其结果是可预测的,但是对于其他线程,它会中断任何正在执行的代码,有可能中断静态对象的生成,造成不可预测的结果。
* 一般来说线程的多少是根据CPU核心来决定的,并不是越多就越好!多开一个线程可能会吃掉1MB的虚拟内存。

*1.增加 线程多入参
*2.增加 获取 回参
*3.增加 设置线程基本属性
*4.增加 VFP启动线程 指令

*20221119V1.02 ( 收费版: 498 )
*1.增加 获取线程X  中的cursor数据
*注意一次性获取Cursor数据集指令需要足够的内存,因为他是一个VFP数组,你不能把将一个非常大的SQlexec结果集放在线程上,(意思就是放小的结果集)
*原因1:线程肯定没有当前VFP进程性能优越。
*原因2:太大的结果集取回需要大的内存支持,当然你可以使用语句像访问当前“cursor.字段”来访问cursor的结果集。
只有本站会员才能查看附件,请 登录


*预期:计划直接不用编译VFP 的DLL,可以直接运行任何一个VFP语句为异步线程指令(理论可行,实际未知。。。)
*
27 回复
#2
foxfans2022-11-19 21:11
vfp2c32.fll  已经开源,里面有多线程,回调函数等开发案例,完全开源免费可控。
只有本站会员才能查看附件,请 登录

https://

[此贴子已经被作者于2022-11-19 21:28编辑过]

#3
sam_jiang2022-11-19 21:55
不知道你这玩意的具体用途,价格不菲啊。。。
#4
iswith2022-11-19 22:14
我知道大楖有!但没有细看他的实现逻辑!你说说原理有些想实现的地方卡着,我也参考参考。。。。。。
#5
csyx2022-11-19 22:50
以下是引用foxfans在2022-11-19 21:11:05的发言:
vfp2c32.fll  已经开源,里面有多线程,回调函数等开发案例,完全开源免费可控。

应该没有可比性
它们都可以吃(vfp 都能用),但原料、味道不一样
vfp2c32 是基于 win32api 的
楼主的产品从名字上看应该是基于 .Net 框架的

不过话说回来,目前的 vfp 版本也被限制在 Windows 平台上,因此区别也就在哪个容易下口、哪个虫子更少
#6
iswith2022-11-19 23:36
以下是引用csyx在2022-11-19 22:50:18的发言:


应该没有可比性
它们都可以吃(vfp 都能用),但原料、味道不一样
vfp2c32 是基于 win32api 的
楼主的产品从名字上看应该是基于 .Net 框架的

不过话说回来,目前的 vfp 版本也被限制在 Windows 平台上,因此区别也就在哪个容易下口、哪个虫子更少

说话好精辟呀!内联ASM C,这个很早就有!大家原理都差不多!个人觉得我写的简单好些!~v~.........,目标不一样
#7
吹水佬2022-11-19 23:58
有没有不通过VFP的COM运行VFP代码的多线程?
如果没有的话,看来实质应该是一样。
简单说就是在线程过程中调用VFP写的控件



#8
foxfans2022-11-19 23:58
DMULT vfp2c32 这些都是开源且方便调用的Vfp多线程库,自己研究琢磨吧,好东西靠研究和封装,这是别人做出来的效果。
只有本站会员才能查看附件,请 登录
#9
吹水佬2022-11-20 00:06
回复 8楼 foxfans
用鼠标按住窗口右上角的关闭“X”不放手拖动看看,如果窗口的东东不会静止就是真正的“不卡”。
#10
schtg2022-11-20 06:28
回复 8楼 foxfans
好!
#11
sostemp2022-11-20 08:59
回复 8楼 foxfans
挺强大的
#12
foxfans2022-11-20 09:04
点X按钮,估计是要这种效果。
只有本站会员才能查看附件,请 登录
#13
iswith2022-11-20 10:03
取个线程结果集看看
#14
foxfans2022-11-20 10:08
不弄了,没时间弄,上面的X号点击不卡就代表是真正意义的多线程。至于返回什么结果和结果集有多种方法,可以构造特定结构,或返回指针+长度,什么内容都可以sys(2600去取)。
#15
吹水佬2022-11-20 12:09
回复 14楼 foxfans
是点按住“X”拖出窗口外才放手,不会关掉窗口的。
拖动时看看有没卡住,这不是说是不是多线程的问题,是测试一下消息的影响,点“X”的消息优先级较高。
#16
iswith2022-11-20 12:17
以下是引用吹水佬在2022-11-20 12:09:54的发言:

是点按住“X”拖出窗口外才放手,不会关掉窗口的。
拖动时看看有没卡住,这不是说是不是多线程的问题,是测试一下消息的影响,点“X”的消息优先级较高。

你不要质疑这个,他是多线的!内联C很早就有,我也有这个源码,但大家目标是不一样的
只有本站会员才能查看附件,请 登录


[此贴子已经被作者于2022-11-20 12:20编辑过]

#17
foxfans2022-11-20 12:27
不卡,很流畅。
多线程主要还是用来后台计算获取数据,再异步回调结果(对Vfp来说可能更多的是cursor)。 高频刷新主界面的应用实际上不会太多,CS下已经没什么优势,大部分转BS了。
只有本站会员才能查看附件,请 登录


[此贴子已经被作者于2022-11-20 12:35编辑过]

#18
吹水佬2022-11-20 12:48
VFP的多线程COM好象是不支持全局定义的东东,就是说在线程过程访问不了全局定义的东东,甚至全局声明的API线程过程也用不了,USE的DBF工作区也一样,在线程过程要再共享打开,不太象是同一进程的东东,反而有象多进程。当然,在进程里创建的线程肯定是多线程。
#19
sych2022-11-20 15:17
非常前卫
#20
sych2022-11-20 15:18
高手切磋,小弟收益
#21
iswith2022-11-20 16:12
没有什么可比性,不用DLL因为那个会改变VFP的习惯            
。。。。。。,你叫他取个结果集回来,一看性能,二看实现简不简单,不要多,就10W行记录
只有本站会员才能查看附件,请 登录


[此贴子已经被作者于2022-11-20 16:20编辑过]

#22
foxfans2022-11-20 16:27
在Vfp WinForm表单上点单X按钮压住不动,试试你的多线程还能不能动。,你这个后台获取数据是基础功,不然失去了多线程的意义,点着还能动,就代表你的其它线程逻辑运行时不受主表单影响,各做各的事,不被影响也不影响主窗口,真正的异步回调结果。另外点击的是你的主表单X,或是VFP开发环境的X不是项目管理器的X。

[此贴子已经被作者于2022-11-20 16:48编辑过]

#23
吹水佬2022-11-20 17:27
以下是引用iswith在2022-11-20 12:17:32的发言:

你不要质疑这个,他是多线的!内联C很早就有,我也有这个源码,但大家目标是不一样的

我没质疑什么吧,只是想测试一下消息性能。
只要是用符合系统线程管理和调度的线程都是多线程吧。
VFP的多线程多年前就测试过,当时玩不出什么兴趣就没继续玩。
核心代码可以贴出(注意,只是测试代码,还没完成,只有简单调用COM方法,属性事件的就还没搞)
VFP测试界面,有定时器记录和3个线程记录
只有本站会员才能查看附件,请 登录

MASM32编写的 ComThread.dll 三个相关文件:
程序代码:

;========================
;ComThread.def
;========================
LIBRARY ComThread.dll
EXPORTS

 _ProcThread



;========================
;ComThread.inc
;========================
include  windows.inc
include  user32.inc
include  kernel32.inc
includelib user32.lib
includelib kernel32.lib

include  ole32.inc
includelib ole32.lib
include  oleaut32.inc
includelib oleaut32.lib
include     debug.inc
includelib debug.lib
include     shlwapi.inc
includelib  shlwapi.lib

DISPPARAMS struct
    rgvarg                   dd ?
    rgdispidNamedArgs        dd ?
    cArgs                    dd ?
    cNamedArgs               dd ?
DISPPARAMS ends

VARIANT struct
    vt                  word  VT_EMPTY
    wReserved1          word 0
    wReserved2          word 0
    wReserved3          word 0
    Union
        lVal            sdword ?
        bVal            word ?  
        iVal            sword ?
        fltVal          real4 ?
        dblVal          real8 ?
        boolVal         word ?  
        scode           dword ?
        cyVal           qword ?
        date            qword ?
        bstrVal         dword ?
        punkVal         dword ?
        pdispVal        dword ?
        parray          dword ?
        pbVal           dword ?
        piVal           dword ?
        plVal           dword ?
        pfltVal         dword ?
        pdblVal         dword ?
        pboolVal        dword ?
        pscode          dword ?
        pcyVal          dword ?
        pdate           dword ?
        pbstrVal        dword ?
        ppunkVal        dword ?
        ppdispVal       dword ?
        pparray         dword ?
        pvarVal         dword ?
        byref           dword ?
        cVal            sbyte ?
        uiVal           word ?  
        ulVal           dword ?
        intVal          sword ?
        uintVal         word ?  
        pdecVal         dword ?
        pcVal           dword ?
        puiVal          dword ?
        pulVal          dword ?
        pintVal         dword ?
        puintVal        dword ?
    ends
VARIANT ends

OBJPARAMS struct

 lpObjName dd ?

 lpFunName dd ?

 lpParams dd ?
OBJPARAMS ends

.const

 IID_IDispatch GUID {00020400h,0000h,0000h,{0C0h,000h,000h,000h,000h,000h,000h,046h}} ;定义IID_IDispatch

.data

 stObjParams OBJPARAMS <>

.data?

 szDefPath db MAX_PATH dup (?)



;========================
;ComThread.asm
;========================
.386
.model flat,stdcall
option casemap:none

include ComThread.inc

.code

DllEntry proc _dhInstance, _ddReason, _ddReserved
  mov eax, TRUE
    ret
DllEntry Endp



 ; 调用对象的函数

 ; lpIDispatch . COM对象指针

 ; ddFunOffset . 函数在函数表的偏移,例如要调用第1个函数,偏移是0*4;第2个函数,偏移是1*4

 ; ddParams .... 参数个数

 ; lpParams .... 参数表指针
_ComInvoke proc c uses ecx edx _lpIDispatch, _ddFunOffset, _ddParams, _lpParams:vararg
  ;参数入栈

 mov ecx, _ddParams

 

 .while ecx >= 1
  dec  ecx
  mov  eax, dword ptr [_lpParams + ecx*4]
  push eax

 .endw

 
  ;调用函数

 mov  eax, _lpIDispatch

 mov  edx, [eax]

 push _lpIDispatch

 mov  eax, _ddFunOffset

 call dword ptr [edx + eax]


 ret
_ComInvoke endp


 ; IDispatch函数调用,传入函数名和参数列表即可以调用,详情可以参考例子。

 ; lpIDispatch . COM对象指针

 ; lpFuncName .. 函数名

 ; lpVar ....... VARIANT结构指针

 ; numArgs .....

 
;_Invoke, lpIDispatch, lpFunName, addr stVar, 1, VT_BSTR, lpParam

_Invoke proc c uses esi edi ebx lpIDispatch, lpFuncName, lpVar, numArgs, args:VARARG ;vartype1,value1,vartype2,value2

 LOCAL hMem:DWORD, tmpMem[128]:BYTE, dispid:DWORD, pointer:DWORD

 LOCAL stDP:DISPPARAMS

 LOCAL IDI_IID_NULL:GUID

 

 invoke RtlZeroMemory, addr IDI_IID_NULL, sizeof GUID

 

 invoke MultiByteToWideChar, CP_ACP, 0, lpFuncName, -1, addr tmpMem, 128

 lea eax, tmpMem

 mov pointer, eax
  ; lpIDispatch.GetIDsOfNames()
  ;  [restricted] Void  GetIDsOfNames (
   ;        [in]  riid  :Ptr GUID,
   ;       [in]  rgszNames :Ptr Ptr I1,
   ;       [in]  cNames :UInt,
   ;       [in]  lcid  :UI4,
   ;       [out] rgdispid :Ptr I4 )

 invoke _ComInvoke, lpIDispatch, 20, 5, addr IDI_IID_NULL, addr pointer, 1, 0, addr dispid
  
  ; (numArgs + 1) * sizeof VARIANT

 mov  eax, numArgs

 inc  eax

 imul eax, sizeof VARIANT  

 
  ; 从堆中分配空间。GPTR...分配固定的内存并初始化为0,返回一个指针

 invoke GlobalAlloc, GPTR, eax
  

 mov hMem, eax
  
  ;init params

 invoke RtlZeroMemory, addr stDP, sizeof DISPPARAMS
  

 .if numArgs > 0
  mov  eax, sizeof VARIANT
  imul eax, numArgs
   
  mov esi, hMem
  lea edi, args
  mov eax, numArgs
  shl eax, 3   ;numArgs*4*2
  sub eax, 8
  add edi, eax
  assume esi:ptr VARIANT
  mov ebx, 0
   
  .while ebx < numArgs
   mov eax, [edi]
   mov [esi].vt, ax
   mov eax, [edi][4]
   mov [esi].lVal, eax
   mov eax, numArgs
   mov stDP.cArgs, eax
   mov eax, hMem
   mov stDP.rgvarg,eax
   add esi,sizeof VARIANT
   sub edi,8
   inc ebx
  .endw
   
  assume esi:nothing
   

 .else
  mov stDP.rgvarg, 0
  mov stDP.cArgs,  0

 .endif
  
  ; lpIDispatch.Invoke()
  ; [restricted] Void  Invoke (
  ;       [in]  dispidMember :I4,
  ;       [in]  riid   :Ptr GUID,
  ;       [in]  lcid   :UI4,
  ;       [in]  wFlags  :UI2,
  ;       [in]  pdispparams :Ptr DISPPARAMS,
  ;       [out] pvarResult :Ptr Variant,
  ;       [out] pexcepinfo :Ptr EXCEPINFO,
  ;       [out] puArgErr  :Ptr UInt )

 invoke _ComInvoke, lpIDispatch, 24, 8, dispid, addr IDI_IID_NULL, 0, 1, addr stDP, lpVar, 0, 0

 invoke GlobalFree, hMem

 ret
_Invoke endp

_ProcThread proc uses esi lpParam

 LOCAL szBuf[MAX_PATH] :BYTE

 LOCAL szGUID   :GUID

 LOCAL lpIDispatch  :DWORD

 LOCAL stVar    :VARIANT

 LOCAL lpPar    :DWORD


 mov esi, lpParam

 assume esi:ptr OBJPARAMS


 invoke CoInitialize, NULL

  ;创建对象

 invoke MultiByteToWideChar, CP_ACP, 0, [esi].lpObjName, -1, addr szBuf, MAX_PATH

 invoke CLSIDFromProgID, addr szBuf, addr szGUID


 .if eax != S_OK
  invoke MessageBox, 0, CTEXT("找不到对象"), 0, 0

 .endif


 invoke CoCreateInstance, addr szGUID, NULL, CLSCTX_INPROC_SERVER or CLSCTX_LOCAL_SERVER, offset IID_IDispatch, addr lpIDispatch


 .if eax != S_OK
  invoke MessageBox, 0, CTEXT("无法创建对象"), 0, 0

 .endif

  ;调用对象函数

 invoke MultiByteToWideChar, CP_ACP, 0, [esi].lpParams, -1, addr szBuf, MAX_PATH

 invoke SysAllocString, addr szBuf

 mov lpPar, eax


 invoke _Invoke, lpIDispatch, [esi].lpFunName, addr stVar, 1, VT_BSTR, lpPar

 invoke SysFreeString, lpPar


 assume esi:nothing

  ;释放对象
  ; lpIDispatch.Release()
  ;  [restricted] UI4  Release (  )

 invoke _ComInvoke, lpIDispatch, 2*4, 0

 invoke CoUninitialize
  

 ret
_ProcThread endp

End DllEntry


#24
foxfans2022-11-20 18:45
主进程线程调度做到不卡,大量业务量的情况时才不会影响整体性能和其它子线程运行过程,如果主调度卡死,其它子线程就成摆设,类似子线程暂停。
只有本站会员才能查看附件,请 登录


[此贴子已经被作者于2022-11-21 23:03编辑过]

#25
iswith2022-11-22 15:40
以下是引用foxfans在2022-11-20 16:27:26的发言:

在Vfp WinForm表单上点单X按钮压住不动,试试你的多线程还能不能动。,你这个后台获取数据是基础功,不然失去了多线程的意义,点着还能动,就代表你的其它线程逻辑运行时不受主表单影响,各做各的事,不被影响也不影响主窗口,真正的异步回调结果。另外点击的是你的主表单X,或是VFP开发环境的X不是项目管理器的X。


在Vfp WinForm表单上点单X按钮压住不动,试试你的多线程还能不能动,说实话你说这话水平还没有那个吹水佬高,那消息机制的问题,跟多线有什么关系。。。。
#26
foxfans2022-11-23 01:17
Vfp主程序展示的界面或控件除了消息还有自绘更新,其它事件等,一旦你主线程一卡死,怎么和多线程没关系,是有很微妙的关系你不懂载前预处理而己。只有先解决自己线程载入问题后,VFP开发的程序+载入运行库才可以像其它语言一样注入其它进程中执行做外挂或其它任务用。别以为传个_vfp 或sys(3095 去docmd处理内容后,IMPLEMENTS就完事,这事只做一半,主表单一卡死你啥都传不回来,只能瞎等,后台任务少还好,极短时间就回过神让人感觉不出卡,任务一多你主程序自己都歇息了还指望他能高频再分配调度,可以用你的net做个简单的线程高频回调看能不能达到上面的效果,上面数值和二维码生成每次变化都是子线程一个计算结果与主进程的交互,其它不废话。如果是做高调度 高并行任务 支持高并发的主表单,那你这多线程要走的路还长,考虑的要更加全面。

[此贴子已经被作者于2022-11-23 07:37编辑过]

#27
吹水佬2022-11-23 07:54
是不是多线程(或有效的多线程),可以简单测试一下:
1、在主程序启动子线程后进入一个死循环,看看子线程的线程过程还能不能正常动作(可以在主程序的死循环里监视线程过程的情况)。
2、在子线程过程里设置一个线程循环过程,线程循环由主程序控制何时跳出结束线程过程。
如果主程序能实时监视子线程过程和控制子线程过程就应该是OK了,注意,控制是“实时”的,每时每刻,想动就动,想停就停。
#28
foxfans2022-11-23 18:54
这些是多线程基本特性
只有本站会员才能查看附件,请 登录



[此贴子已经被作者于2022-11-23 19:32编辑过]

1