win.involk设置线程超时时间

By popy32 at 2021-12-23 • 1人收藏 • 795人看过

编写多线程界面程序时,win.involk可以说是经常用到的函数,多线程带返回值,不卡界面。

import win.ui;
/*DSG{{*/
var winform = win.form(text="aardio form";right=319;bottom=159)
winform.add(
button={cls="button";text="访问网页";left=200;top=112;right=304;bottom=144;z=1};
edit={cls="edit";left=8;top=8;right=304;bottom=104;edge=1;multiline=1;z=2}
)
/*}}*/

showHttpData = function(){
	var r = win.invoke(
		function(){
			import win;
			import inet.http;
			var url = "https://www.qq.com";
			var ohttp = inet.http();
			var d = ohttp.get(url);
			var t = d;//web.json.tryParse(d);
			return t;
		} 
	)
	if(r == null ) return "";
	return r;
}

winform.button.oncommand = function(id,event){
	winform.button.disabled = true;
	winform.edit.text = showHttpData();
	winform.button.disabled = false;
}

winform.show();
win.loopMessage();


但有个需求是线程超时返回,例如访问网页指定超时退出(当然inet可以配置超时参数)。如果遇到线程卡住的时候

import win.ui;
/*DSG{{*/
var winform = win.form(text="aardio form";right=319;bottom=159)
winform.add(
button={cls="button";text="访问网页";left=200;top=112;right=304;bottom=144;z=1};
edit={cls="edit";left=8;top=8;right=304;bottom=104;edge=1;multiline=1;z=2}
)
/*}}*/

showHttpData = function(){
	var r = win.invoke(
		function(){
			import win;
			win.delay(4500);
			return "4500ms";
		} 
	)
	if(r == null ) return "";
	return r;
}

winform.button.oncommand = function(id,event){
	winform.button.disabled = true;
	winform.edit.text = showHttpData();
	winform.button.disabled = false;
}

winform.show();
win.loopMessage();


关注到thread.wait函数(lib/preload/thread.aardio)是自带超时退出的,于是试着给win.involk加上超时参数。


win.involk的实现

win.involk 实际调用的是 thread.invokeAndWait。相关的调用栈为

invokeAndWait -> waitOne -> threadwait

可以注意到threadwait实际上由超时处理的,只是invokeAndWait并没有传入超时参数,于是尝试重写invokeAndWait,增加参数超时时间timeout

namespace thread {
    var _id_invoke = {};
    invokeAndWait2 = function(timeout, func,... ){
    	var id = ..table.push(_id_invoke,1);
    	var name = ..string.format("$(_winvoke:[tid:%d][%d]",..thread.getId(),id );
	
		var h = create(
			function(func,name,...){ 
				..thread.set(name, null);
				var ret = { func(...) }
				..thread.set(name, ret);
			},func,name,...
		); 
		
		if(timeout == 0) {
		    timeout = null;
		}
		
		waitOne(h, timeout); 
	 	//wait(h, timeout); 
	 	//waitClose(h, timeout); 
	 	
		var ret = get(name);
		_id_invoke[id] = null;
		..raw.closehandle(h);
	 	
		if(ret){
			return ..table.unpackArgs(ret);
		}
	}
}


完整代码

import win.ui;
/*DSG{{*/
var winform = win.form(text="aardio form";right=319;bottom=159)
winform.add(
button={cls="button";text="访问网页";left=200;top=112;right=304;bottom=144;z=1};
edit={cls="edit";left=8;top=8;right=304;bottom=104;edge=1;multiline=1;z=2}
)
/*}}*/

namespace thread {
	var _id_invoke = {};
    invokeAndWait2 = function(timeout, func,... ){
    	var id = ..table.push(_id_invoke,1);
    	var name = ..string.format("$(_winvoke:[tid:%d][%d]",..thread.getId(),id );
	
		var h = create(
			function(func,name,...){ 
				..thread.set(name, null);
				var ret = { func(...) }
				..thread.set(name, ret);
			},func,name,...
		); 
		
		if(timeout == 0) {
		    timeout = null;
		}
		
		waitOne(h, timeout); 
	 	//wait(h, timeout); 
	 	//waitClose(h, timeout); 
	 	
		var ret = get(name);
		_id_invoke[id] = null;
		..raw.closehandle(h);
	 	
		if(ret){
			return ..table.unpackArgs(ret);
		}
	}
}

import time.performance;
showHttpData = function(){
	var tk1 = time.performance.tick();
	var r = thread.invokeAndWait2( 500, 
		function(){
			import win;
			win.delay(4500);
			return "";
		} 
	)
	var tk2 = time.performance.tick();
	winform.edit.print( tostring(tk2 - tk1) ,"ms");
	
	if(r == null ) return "";
	return r;
}

winform.button.oncommand = function(id,event){
	winform.button.disabled = true;
	//winform.edit.text = "";
	showHttpData()
	//winform.edit.print(  );
	winform.button.disabled = false;
}

winform.show();
win.loopMessage();


改进后的invokeAndWait2确实可以超时退出了,同时窗口也不会卡死,说明能响应窗口消息,但不停的拖放窗口,移动鼠标这些操作(相当于不停的发送)后发现,invokeAndWait2不能按照指定的超时时间timeout返回,需要等鼠标稳定后很长时间才能返回。


invokeAndWait2设置的是500ms,但实际和500ms有较大偏差


invokeAndWait2.gif


改进threadwait


继续研究threadwait的处理逻辑,threadwait主要过程是参数解析(获得需要wait的线程,手动自动handle),涉及消息处理的是API是 msgWaitForMultipleObjects,如果在threadwait过程中一种有窗口消息,流程将在do while中不断循环。


这时如果msgWaitForMultipleObjects以经过timeout超时后返回,又将在循环里继续运行,导致重复的timeout时间。处理方式也很简单,用last记录剩余的超时时间,time.performance计算实际运行的时间。


// 已修改过的threadwait 
var threadwait = function( bClose,bAll, ...){
    var threads,timeout = ...;
    if(type(threads)!="table") {
    	timeout = 0xFFFFFFFF
    	threads ={...}
    	if( type(threads[#threads]) == "number" ){
    		timeout = ..table.pop(threads,1)
    	}
    }
    elseif(timeout === null ) timeout = 0xFFFFFFFF  /* Infinite timeout*/
    	
    var len = #threads
    if(!len) error("参数未指定线程句柄",3);
    var threads_c = ..raw.toarray( threads ,"pointer" ,"array");
    var msg,peek,parse,hasMsg;  
    
    var last = timeout; //剩余
    
    var ret; 
    if( (!bAll) && ..win[["_form"]] ){
    	msg = ::MSG();
		parse = ..win._parseMessage;
		delay = ..win.delay;
		peek = ..__messagePeek; 
		
    	do{
    	                var tk1 = ..time.performance.tick();
			ret = msgWaitForMultipleObjects(len,threads_c,bAll,last , 0x4FF/*_QS_ALLINPUT*/); 
			//
			
/*
			last = last - ..math.floor(tk2 - tk1);
			
			if(last <= 0) {
				break;
				//..win.quitMessage(msg.wParam);
				//return null;
			}
*/
			
			if( ret!=len ) break;
			hasMsg =  peek(msg); 
			if(hasMsg) {
				parse(msg);
				//delay(100);
			}
			elseif(hasMsg===null){
				..win.quitMessage(msg.wParam);
			}
			var tk2 = ..time.performance.tick();
			last = last - ..math.floor(tk2 - tk1);
			
			if(last <= 0) {
				break;
			}
		}while( hasMsg!==null) 
    }  
    else {
    	ret = waitForMultipleObjects(len,threads_c,bAll,timeout, 0x4FF/*_QS_ALLINPUT*/);  
    }
    // 后面的代码省略


改进后的效果


invokeAndWait21.gif


效果比之前好了一些,和期望的timeout更接近,但窗口有太多消息时仍然会一直等待到消息结束,大家如果有更好的方案欢迎讨论。



2 个回复 | 最后更新于 2021-12-24
2021-12-23   #1

win.invoke带个win就是因为工作在窗口线程, 会被消息阻塞, 你这个时候就需要直接用thread.create, thread.wait就好了吧, 也不卡界面, 干嘛一定要用win.invoke? 在界面线程里工作?

2021-12-23   #2

回复#1 @admin :


文章里写了,win.invoke实际调用的时 thread.invokeAndWait,并不是窗口线程处理事情。


本文重点其实是想分享,线程超时退出的实现。标题不限于win.invoke设置超时,换成thread.involk,thread.create都可以。


另外thread.wait 测试中卡了界面 ,只能用thread.waitone,原理就在thread.threadwait这个函数。窗口事件,超时的实现都在thread.threadwait。

登录后方可回帖

登 录
信息栏
 私人小站

本站域名

ChengXu.XYZ

投诉联系:  popdes@126.com



快速上位机开发学习,本站主要记录了学习过程中遇到的问题和解决办法及上位机代码分享

这里主要专注于学习交流和经验分享.
纯私人站,当笔记本用的,学到哪写到哪.
如果侵权,联系 Popdes@126.com

友情链接
Aardio官方
Aardio资源网


才仁机械


网站地图SiteMap

Loading...