线程间传递比较大的结构数组的最佳方案

By admin at 12 天前 • 0人收藏 • 274人看过

最近遇到个问题:

上次说到那个光纤分析仪的dll里会返回一个结构体 , 内部有两个记录了7500个位置的数组, 

需要在一个线程里连续读取这个dll的返回数组, 然后更新并返回给其他线程使用.

之前用thread.set("pos1",table_A)来传递到其他线程, 测试结果发现这句话需要执行30毫秒左右,然后我换成thread.table, 执行结果20毫秒左右,感觉这种方式效率比较低, 那么有没有其他方式?

经过我的各种测试,最终得出的结论如下:

答案:使用raw.copy()直接操作指针. 时间基本上在1ms以内了.


image.png


import win.ui;
/*DSG{{*/
var winform = win.form(text="aardio form";right=759;bottom=469)
winform.add(
button2={cls="button";text="(连续) 线程中修改数值";left=71;top=78;right=319;bottom=157;font=LOGFONT(h=-16);z=1};
button3={cls="button";text="界面线程中获取数值";left=413;top=77;right=661;bottom=156;font=LOGFONT(h=-16);z=2};
button4={cls="button";text="其他线程中获取数值";left=413;top=202;right=661;bottom=281;font=LOGFONT(h=-16);z=3};
button5={cls="button";text="停止连续循环";left=71;top=171;right=319;bottom=235;z=4}
)
/*}}*/

import time.performance;
import console
console.open()
 
var struct_A = {float d[7500]={}};
var globalTab = raw.buffer(struct_A);
//初始化指针
var P_globalTab = raw.toPointer(globalTab);

//第一步设定指针
thread.set("全局线程结构体指针", P_globalTab);
 
winform.button2.oncommand = function(id,event){
    thread.set("手动停止", false);
    thread.invoke( 
        function(){
            import console
            import time.performance;
             
            var demo_B = {float d[7500]={}};
            //获取数据指针地址
            var P_Tab = thread.get("全局线程结构体指针");
            while(!thread.get("手动停止")){
                //模拟更新数据
                for(i=1;7500;1){
                    demo_B.d[i] = math.random();
                }
                var t1 = time.performance.tick();
                //数据复制到指针里
                raw.copy(P_Tab,demo_B);
                
                console.log("raw.copy(P_Tab,demo_B)耗时",time.performance.tick()-t1)
                console.log("连续线程中修改为demo_B.d[1]",demo_B.d[1]);
                console.log("连续线程中修改为demo_B.d[2]",demo_B.d[2]);
                 
                sleep(1000);//模拟一秒更新一次
            }
        }
    )
}
 
winform.button5.oncommand = function(id,event){
    //关闭模拟定时更新
    thread.set("手动停止", true);
}
 
winform.button3.oncommand = function(id,event){
    //界面线程中耗时比较大
    var t1=time.performance.tick();
    
    var demo_struct = raw.convert(P_globalTab,struct_A);
    console.log("界面线程中获取数值d[1]",demo_struct.d[1])
    console.log("界面线程中获取数值d[2]",demo_struct.d[2])
    
    console.log("界面线程中耗时比较大",time.performance.tick()-t1);
}
 
winform.button4.oncommand = function(id,event){
    //工作线程里操作耗时小
    thread.invoke( 
        function(){
            import console
            import time.performance;
            
            var t1=time.performance.tick();
            
            //获取数据指针地址
            var P_Tab = thread.get("全局线程结构体指针");
            //因为我的数据格式都是浮点型float, 所以有两种转换方式
/*          //第一种方式,直接转换为给定的结构体,适合于数据类型不相同的情况
            //要转换的结构体定义
            var struct_A = {float d[7500]={}};
            var demo1_struct = raw.convert(P_Tab,struct_A);
            console.log("其他线程中获取数值d[1]",demo1_struct.d[1])
            console.log("其他线程中获取数值d[2]",demo1_struct.d[2])
*/
            //第二种方式,转换为制定的数组float,适合于数据类型一致的情况
            var demo2_array = raw.convertArray(P_Tab,7500,"float");
            console.log("其他线程中获取数值d[1]",demo2_array[1])
            console.log("其他线程中获取数值d[2]",demo2_array[2])
            
            
            console.log("工作线程里操作耗时小",time.performance.tick()-t1);
        }
    )
}
 
winform.show();
win.loopMessage();





这里附上之前测试thread.table的代码 , 时间大概在21毫秒.

import console; 
console.open()

import time.performance;
var numA = {float d[7500]={}};
var struct_numA = {float d[7500]={}};
for(i=1;7500;1){
	numA.d[i]=math.random();
}
console.log("numA.d[1]",numA.d[1])
console.log("numA.d[2]",numA.d[2])
import thread.table;
var thrdTable = thread.table("线程共享表名称",true/*清空*/ );


//初始化并修改值
winform.button5.oncommand = function(id,event){
	var t1 = time.performance.tick()
	//混入表
	table.assign(thrdTable,numA.d)
	console.log("混入表耗时",time.performance.tick()-t1)
}

thread.table内部用到的仍然是thread.set() , 所以效率基本没变.

image.png

1 个回复 | 最后更新于 3 天前
3 天前   #1

这个测试存在几处不合理:

1、耗时实际是错的,上面代码是先多次调用 console.log ,然后再去计算耗时, 而控制台读写是非常慢的,这是测试常犯的错误。实际上先计算时间,耗时就从 100 多毫秒降到了 零点几毫秒。

2、你的代码通过内存直接复制值,但是你忽略了一个东西,aardio 的共享表这些方案是有自动加锁并保证线程安全,而你没有加线程锁,这当然会快多了,你想像一下,十字路口关掉线绿灯能不快吗?!

实际上这个耗时一般可以忽略,例如我们跨进程交互时,用RPC方案,那个要慢很多,但使用中基本感觉不出来,因为在程序设计时,一般可以回避多余的线程、或进程间交互。

实际上标准库中的 raw.struct 可以实现自动 raw.copy 结构体,我晚上改进了一下,让这个对象可以跨线程共享指针,并且优化了数组读写操作,基于新版 raw.struct 我重写了一下你的例子,界面或线程读取共享数组值的耗时为 0.00X 毫秒( 实际上应当更快,因为调用time.performance.tick也需要时间)

import win.ui;
/*DSG{{*/
var winform = win.form(text="aardio form";right=759;bottom=469)
winform.add(
button2={cls="button";text="(连续) 线程中修改数值";left=71;top=78;right=319;bottom=157;font=LOGFONT(h=-16);z=1};
button3={cls="button";text="界面线程中获取数值";left=413;top=77;right=661;bottom=156;font=LOGFONT(h=-16);z=2};
button4={cls="button";text="其他线程中获取数值";left=413;top=202;right=661;bottom=281;font=LOGFONT(h=-16);z=3};
button5={cls="button";text="停止连续循环";left=71;top=171;right=319;bottom=235;z=4}
)
/*}}*/
 
import raw.struct;
var FloatArray = raw.struct({float d[7500]={}});
var floatArray = FloatArray();
  
winform.button2.oncommand = function(id,event){
    thread.set("手动停止", false);
    thread.invoke( 
        function(floatArray){
            import console
            import time.performance;
               
            while(!thread.get("手动停止")){
                //模拟更新数据
                var arr = {};
                for(i=1;7500;1){
                    arr[i] = math.random();
                }
                var t1 = time.performance.tick()
                
                //数据复制到指针里
                floatArray.d = arr;
                t1 = time.performance.tick()-t1; 
                
                console.log("更新全部值耗时",t1)
                console.log("连续线程中修改为demo_B.d[1]",floatArray[1]);
                console.log("连续线程中修改为demo_B.d[2]",floatArray[2]);
                  
                sleep(1000);//模拟一秒更新一次
            }
        },floatArray
    )
}
  
winform.button5.oncommand = function(id,event){
    //关闭模拟定时更新
    thread.set("手动停止", true);
}
  
winform.button3.oncommand = function(id,event){ 
    import time.performance;
    import console;
    
    var t1=time.performance.tick();
    var a,b = floatArray[1],floatArray[2];
    t1 = time.performance.tick()-t1; 
    
    console.log("界面线程中获取数值d[1]",a)
    console.log("界面线程中获取数值d[2]",b) 
    console.log("界面线程中耗时并不大",t1);
}
  
winform.button4.oncommand = function(id,event){
    //工作线程里操作耗时小
    thread.invoke( 
        function(floatArray){
            import console
            import time.performance;
             
            var t1=time.performance.tick();
            var a,b = floatArray[1],floatArray[2];
            t1 = time.performance.tick()-t1; 
            
            console.log("其他线程中获取数值d[1]",a)
            console.log("其他线程中获取数值d[2]",b)
             
            console.log("工作线程耗时",t1);
        },floatArray
    )
}
  
winform.show();
win.loopMessage();


登录后方可回帖

登 录
信息栏
本站永久域名:HtmLayout.Cn
纯私人站,当笔记本用的,学到哪写到哪,目前在学aardio+halcon机器视觉.
Aardio 官方站:Aardio官方
Aardio最新功能:Aardio官方更新日志
苏扬博客:苏扬博客
C大Aardio论坛:Aar爱好者论坛
简码教程网:简码编程
AARDIO语言QQ群:70517368
本 站 主 站:Stm32cube中文网
Htmlayout界面在线学习文档
Sciter中文在线文档Sciter在线学习文档
aardio在线手册Aardio在线手册

赞助商:才仁机械
下载站:非凡软件站
Loading...