//没登陆

欢迎您来到凯恩之角,奈非天!

帖子:319

符文:70

发表于 2019-9-26 22:02:58 |显示全部楼层 来自:江苏
本帖最后由 雪落丶丿寒 于 2020-8-21 00:11 编辑

关于框架的教程,已发到另一个帖子致各位罗技宏作者,关于多线程框架的入门教学
众所周知,罗技宏在业务逻辑上是单线程的,对于暗黑宏的开发,主要体现在以下两个问题上:
        1.宏开始后无法干预
                按下g键后,宏开始运行,按下第二次g键时,若宏没有停止,则第二次g按键会被挂起,直到前一次宏退出时,才会执行此次g按键。这就导致了,在宏运行的过程中,不能直接的使用g按键对其进行干涉,做不到宏的'随开随停';
        2.逻辑流错综复杂
                举个例子,对于奶僧来说,进本后需要一直保持真言和黑人的释放,而在定点后需要打定点的宏,此时的两宏并发,在逻辑上非常简单, 但在实现上就比较麻烦了,两段代码糅杂在一起,非常混乱。这就是两条逻辑流交错时,逻辑流(双线)与开发流(单线)不一致,导致代码变得复杂冗余、高耦合。
  那么有什么解决办法么?
  以上两点都是单线程的弊病,那么自然可以想到,把程序变成多线程不就行了嘛~
  可惜,罗技是不支持多线程的,多线程是基于cpu不断的切换上下文,而达到宏观上的线程并发,不是发生在代码层面的,所以无法通过代码去实现真正的多线程,这时候就需要曲线救国一下了
  先提两个知识点:
  1.轮询:
        什么是轮询呢,顾名思义就是循环着去询问消息队列,是否有事情做。前文提到过,在用户按下第二次g键后,需要等待前一次宏的运行完毕才能接着运行。轮询的处理则是将一次宏的时间分割成多段,每次进来只运行一个较短的轮询时间就退出,这样用户的命令就可以通过轮询的间隙发出来。
  2.协程:
        协程运作不同于多线程,他是发生在代码层面的(也就是说即使lua不支持协程运作,也可以通过代码去实现),协程中记录一条完整的逻辑流,协程之间的切换就行互调方法一样简单、高效,宏开发者只需要将不同的逻辑流写到不同的协程中去,则只需要关心‘在什么时候切换’,而不需要关心‘怎么切换’的问题了。

  说完两个知识点,下面就要说说所谓的‘多线程’实现了
  其实运用轮询和协程实现并发的宏框架目前是有的,不过暗黑的宏作者很少有人用,目前此框架拥有这以下两个问题,导致其只能实现简单的连 点器功能,并不普适。
  1.在协程中写Sleep函数
        在协程中写Sleep函数并不会让协程放弃系统资源,Sleep函数依旧会让整个程序停下来等他,这就使得在轮询的低时间高频率的背景下,不能做循环节较长的宏,多协程并发时,时间不精准。
  2.每次轮询都遍历一遍待办任务表
        这个是很消耗资源的事情,直接导致了轮询时间不能设置的太短,宏的灵敏性不够,其实并不需要去遍历整个消息队列,只需要找到时间最接近的任务去执行就行了。

  那么这两个问题又怎么去解决呢
  1.将Sleep函数完全抽离到主线程里来,所有的逻辑流交于协程去运作,协程运作到sleep函数时将其挂起,并向消息队列中插入一则(当前时间+需sleep时间)的任务,等待时间到时将其唤醒继续运行。
  2.目前消息队列的数据模型只是lua原生的表,我在框架中重新构建了一个插排队列的数据模型,任务插入时排序,每次轮询时从队列首端(时间最近)取值。

  ok,前言说了那么多,正文要来了,在此发一个我搭建的“多线程”框架。
  具体实现细节就先不讲了,说说使用方法吧



workspace.7z

6.31 KB, 下载次数: 335

帖子:56

符文:8

2#
方法呢?懵逼的我懵逼的问

点评

雪落丶丿寒  实在是凯恩的帖子编辑器太难用了,适应中,哈哈,稍安勿躁  发表于 2019-9-26 22:20
发表于 2019-9-26 22:07:56 来自凯恩之角App |显示全部楼层 来自:内蒙古

帖子:612

符文:143

3#
前排支持。希望大佬研究

点评

雪落丶丿寒  谢谢支持  发表于 2019-9-26 23:08
发表于 2019-9-26 22:10:13 来自凯恩之角App |显示全部楼层 来自:黑龙江

帖子:722

符文:40

4#
大佬666666
发表于 2019-9-26 22:18:03 |显示全部楼层 来自:辽宁

帖子:319

符文:70

5#
1569507064(1).jpg

第一步,将压缩包下载下来解压后,在代码里引入框架(我是直接放桌面了)
第二步,写一个init函数
第三步,调用main函数插入键位指令main(要运行的函数,绑定的键位,按下还是抬起‘默认按下’,函数的参数‘如果有的话’)

如上,则实现了按下g5控制台输出‘hello world!’
发表于 2019-9-26 22:19:15 |显示全部楼层 来自:浙江

帖子:319

符文:70

6#
1569508036(1).jpg

当然,不适应这种写法的朋友,也可以重写main方法,这样基本和原来的方法差不多了
发表于 2019-9-26 22:30:11 |显示全部楼层 来自:江苏

帖子:319

符文:70

7#
1569509830(1).jpg

以上,将序列写到一个方法中,在使用控制器统一控制,此控制器使得第一次调用开启序列,第二次调用关闭序列,由于不具有普适性,我没有将此控制器放入框架中,如果有这个需求的朋友可以自行放到框架里。
对其中涉及到的框架里的方法做一个api:
1.sleep()
一切用到Sleep函数的地方,请使用小写的sleep代替
2.run()
参数为方法,run函数执行此方法,返回值为此方法运行的协程对象
3.list:delete()
参数为协程对象,将该协程删除
发表于 2019-9-26 23:05:02 |显示全部楼层 来自:江苏

帖子:319

符文:70

8#
以上方法将逻辑流和开发流彻底分离,可以看到序列方法里写的是纯序列,不需要每次运行时写一大堆判断语句并且跟各种指示灯挂钩云云,可以从外界直接中断此序列并执行其他任务。
至此,框架的基本方法就已经结束了,下面是我根据框架集成的两个比较好用的方法。
1.多线程连点器:
1569510861(1).jpg

列表内的所有按键完全隔离,实现了一个连点器的功能,在此实例中,奶僧的三个序列由按键统一调配,可读性较高。
1)clicks()
参数为一个数组,此函数将数组解析为多条序列的统一控制器,返回一个控制器函数
此函数参数为控制命令
“start”:开启宏
“end”:关闭宏
“start_end”:若开则关,若关则开
2)作为参数的数组序列
{
{键位,连点间隔,按下抬起间隔(默认50ms)},
{开启方法, 结束方法}--开宏时运行开启方法,关宏时运行结束方法
}
可见,此连点器支持解析函数,所以可以用作多线程共同控制的接口
数组式编程,让多线程的调配运行代码可读性显著提高
发表于 2019-9-26 23:40:11 |显示全部楼层 来自:浙江

帖子:319

符文:70

9#
第二个方法明天再发吧,顺便提一下,重新封装的sleep会挂起当前线程,自然是无法在主线程里使用的,所以需要测试的朋友请将测试代码写到函数里,用run函数调用
发表于 2019-9-26 23:48:26 来自凯恩之角App |显示全部楼层 来自:江苏

帖子:1192

符文:24

10#
spring.lua 打開是空的。。。

发表于 2019-9-27 00:17:40 |显示全部楼层 来自:广东

帖子:3764

符文:396

11#
学习                     
发表于 2019-9-27 00:32:59 |显示全部楼层 来自:北京

帖子:77

符文:41

12#
厉害,虽然看不懂
发表于 2019-9-27 04:28:52 来自凯恩之角App |显示全部楼层 来自:山东

帖子:211

符文:26

13#
给技术流点个赞,一看就是专业程序猿

点评

雪落丶丿寒  谢谢~  发表于 2019-9-27 08:41
发表于 2019-9-27 06:45:30 来自凯恩之角App |显示全部楼层 来自:重庆

帖子:319

符文:70

14#
iVoxing 发表于 2019-9-27 00:17
spring.lua 打開是空的。。。

奇怪我这里打开没啥问题呀,我直接传一份代码吧。
发表于 2019-9-27 06:45:53 来自凯恩之角App |显示全部楼层 来自:浙江

帖子:319

符文:70

15#
  1. function init()end--init初始化

  2. -- 主控制器
  3. function controller()
  4.         local array = {}
  5.         return function(func,g_num,event,...)
  6.                 if func==nil then
  7.                         return (array[g_num]==nil and {nil}or{array[g_num][event]})[1];
  8.                 end
  9.                 if event==nil or event==1 then event = 'MOUSE_BUTTON_PRESSED' end;
  10.                 if event==1 then event = 'MOUSE_BUTTON_PRESSED' end;
  11.                 if array[g_num]==nil then array[g_num]={}end;
  12.                 array[g_num][event]={func,{ ... }};
  13.         end
  14. end
  15. --多线程连点器
  16. --[[
  17. @description 将序列数组整理成多个自旋对象,并进行统一调度
  18. @author        残叶00         2019/9/2
  19. @parms        序列数组
  20.         {
  21.                 {按键,                按键间隔,        按下与抬起时间间隔(默认50ms),                拦截器函数},
  22.                 {"q",                20000                },                                                        --每20s点一次q
  23.                     {3,                1000,                100,                                          click_arr_w},        --每秒点击一次鼠标右键并按照click_arr_w函数拦截

  24.                 {开始执行函数,                结束执行函数},
  25.                 {start_fun,                end_fun        }
  26.         }

  27.         拦截器模板
  28.         function click_arr_w(get)                               
  29.                 if GetRunningTime()-get(1)>=15000 then
  30.                         log('wait');
  31.                         return true;
  32.                 else return false;
  33.         end end

  34. @return         连点器对象
  35.                 @parms        --        'start'        --开启连点               
  36.                         --        'end'        --关闭连点                        --@return end_func的返回值
  37.                         --        'status'--当前序列开启状态                --@return 运行中true
  38.                         --        数字        --根据下标获取自旋数组对应对象        --@return 自旋对象
  39. ]]
  40. function clicks(array)
  41.         local flag=false
  42.         local spin_arr={}
  43.         local arr=array;
  44.         local function main_fun(arg)
  45.                 if arg=='start_end' then arg=(flag and'end'or'start')end -- 开关
  46.                 if arg=='start' and flag==false then -- 开启
  47.                         for i=1,#array do
  48.                                 if(type(array[i])=="function")then run(array[i])
  49.                                 elseif (type(array[i])=="table"and type(array[i][1])=="function")then run(array[i][1])
  50.                                 elseif (type(array[i])=="table")then
  51.                                         local key = array[i][1];--按键
  52.                                         local between_p_r = array[i][3];--按下抬起间隔
  53.                                         if between_p_r==nil then between_p_r=50 end;--默认50ms间隔
  54.                                         local sleep_time = array[i][2]-between_p_r;--间隔
  55.                                         local case_when = array[i][4];--阻隔器
  56.                                         if(case_when==nil)then case_when=function()return false;end;end;
  57.                                         local function click(s)
  58.                                                 local flag,time = case_when(main_fun);
  59.                                                 if flag then
  60.                                                         sleep(time or sleep_time+between_p_r)return s;
  61.                                                 end
  62.                                                 press(key);
  63.                                                 sleep(between_p_r);
  64.                                                 release(key);
  65.                                                 return GetRunningTime();
  66.                                         end
  67.                                         local obj_spin = spin(click,sleep_time,GetRunningTime(),release,key);
  68.                                         spin_arr[i]=obj_spin
  69.                                         obj_spin("start");
  70.                                 end
  71.                         end
  72.                         flag=true;
  73.                 elseif arg=='end'and flag then -- 关闭
  74.                         for i=1,#array do
  75.                                 if(type(array[i])=="table" and type(array[i][2])=="function")then run(array[i][2])
  76.                                 elseif (type(array[i])=="table")then
  77.                                         spin_arr[i]("end");
  78.                                 end
  79.                         end
  80.                         flag=false
  81.                         spin_arr={};
  82.                 elseif arg=='status' then return flag; -- 返回运行状态

  83.                 elseif type(arg)=='number' then
  84.                         return spin_arr[arg]and spin_arr[arg]('get')or GetRunningTime(); -- 返回自旋对象
  85.                 end
  86.         end
  87.         return main_fun;
  88. end
  89. --多线程连点器 end ---
  90. --序列运行器
  91. function Queue (array,start_func,end_func,end_queue_func)
  92.         local runningTime;
  93.         local step;
  94.         local flag=false;
  95.         local function do_queue(v_step)--主序列
  96.                 v_step = step;
  97.                 if array[v_step]==nil then --序列运行到底
  98.                         if end_queue_func==nil then v_step=1; -- 若序列结束函数为空,则重置序列
  99.                         else end_queue_func(v_step); end;        --        否则执行序列结束函数;
  100.                 else        --未运行到底,则正常运行
  101.                         runningTime = GetRunningTime();
  102.                         array[v_step][1](array[v_step][2]);
  103.                         v_step=v_step+1
  104.                 end
  105.                 step=v_step;
  106.                 return v_step; --运行下一步
  107.         end
  108.         local function sleep_func()--sleep函数
  109.                 if array[step]==nil then return 0;
  110.                 else return array[step][3]end;
  111.         end
  112.         local que_spin = spin(do_queue,sleep_func,1,end_func);--生成自旋对象
  113.         return function(arg)
  114.                 if arg=='start_end' then arg=(flag and'end'or'start')end -- 开关
  115.                 if arg=='start' and flag==false then
  116.                         flag = true
  117.                         run(function()
  118.                                 if start_func then start_func()end;--执行初始函数
  119.                                 que_spin("start");        --执行序列
  120.                         end);
  121.                 elseif arg=='end' and flag==true then
  122.                         flag = false
  123.                         run(function()que_spin("end");end);--关闭序列
  124.                 elseif arg=='status' then return flag;
  125.                 elseif arg=='get' then return step;
  126.                 elseif type(arg)=='number'then step=arg;--修改步数
  127.                 end


  128.         end
  129. end


  130. -- 轮询初始化
  131. function InitPolling()
  132.         M_State = GetMKeyState("mouse");
  133.         SetMKeyState(M_State, "mouse");
  134.         poll_step = 1 ;--轮询时间片
  135. end
  136.   
  137. ----------------------------主程序----------------------------
  138. function OnEvent(event,arg,family)
  139.         if event == "PROFILE_ACTIVATED" then
  140.                 --  初始化
  141.                 ClearLog();--清空控制台
  142.                    InitPolling();--轮询初始化
  143.                    list=List:new();--数组初始化
  144.                   spin(gc,10*60*1000)('start');--10min调用一次gc回收资源
  145.                 main = main or controller();
  146.                    init();--用户配置初始化
  147.         elseif family == "mouse" and (event == "M_RELEASED"or event=="M_PRESSED") then
  148.         else
  149.                 --从按键配置表里读取
  150.                 local fun_arr = main(nil,arg,event);
  151.                 if fun_arr then fun_arr[1](unpack(fun_arr[2]))end
  152.         end
  153.         if family == "mouse" and event == "M_RELEASED" then
  154.                 Poll(event,arg,family);
  155.         end
  156. end

  157. -- 轮询
  158. function Poll(event, arg, family)
  159.         local next_thread = list:get();
  160.         while next_thread do
  161.                 coroutine.resume(next_thread);
  162.                 next_thread = list:get();
  163.         end
  164.         Sleep(poll_step);
  165.         SetMKeyState(M_State, "mouse");
  166.    
  167. end


  168. --sleep方法
  169. function sleep(time)
  170.         if type(time)~="number"then log('error: sleep parm error');coroutine.yield();return end;
  171.         local now_thread = coroutine.running();
  172.         local next_time = GetRunningTime()+time;
  173.         list:add(next_time,now_thread);
  174.         coroutine.yield();
  175. end


  176. --重写log调试
  177. function log(...)
  178.         local str ="";
  179.         for i = 1,#arg do str=str..tostring(arg[i]);end
  180.         OutputLogMessage(" %s \n",tostring(str))
  181. end

  182. --添加线程,并立即调用
  183. --传入参数为方法
  184. function run(func,...)
  185.         if type(func)~="function"then  log('error: run parm error');return nil end
  186.         local cor = coroutine.create(func,...);
  187.         list:add(-1,cor);
  188.         return cor;
  189. end
  190. --按键
  191. function press(_arg)
  192.         if type(_arg)=="number" then PressMouseButton(_arg)elseif type(_arg)=="string" then PressKey(_arg)end
  193. end
  194. function release(_arg)
  195.         if type(_arg)=="number" then ReleaseMouseButton(_arg)elseif type(_arg)=="string" then ReleaseKey(_arg)end
  196. end

  197. --垃圾回收
  198. function gc()
  199.         --log('before gc:',collectgarbage("count"),'kb');
  200.         collectgarbage("collect");
  201.         --log('after gc:',collectgarbage("count"),'kb');
  202. end
  203. --自旋方法
  204. --[[
  205. @author        残叶00         2019/9/1
  206. @parms        func 为循环调用的方法(禁止在func里使用sleep函数)        --@function func(parm) ...;  return parm; end
  207.                 time 为循环时间
  208.                 init 为循环参数初始值 可省
  209.                 end_func 循环终f止后调用的方法 可省
  210.                 ... 循环终止后调用的方法参数 可省

  211. @return         spin 对象
  212.                 @parms        --        'start'        --开启自旋               
  213.                         --        'end'        --关闭自旋并调用end_func                --@return end_func的返回值
  214.                         --        'status'--当前自旋开启状态                        --@return 运行中true
  215.                         --        'get'        --当前自旋状态参数                        --@return 循环方法的参数
  216. ]]
  217. function spin(func,time,init,end_func,...)
  218.         local flag = false;
  219.         local cor ;
  220.         local function new_fun(func,init,time,end_func,...)
  221.                 local i = init ;
  222.                 local args={ ... }
  223.                 return function(arg)
  224.                         if arg == 1 then return i;
  225.                         elseif arg == 0 then return (end_func and{end_func(unpack(args))}or{nil})[1] end;
  226.                         while true do               
  227.                                 i=func(i);
  228.                                 local t = (type(time)=="function"and{time()}or{time})[1]
  229.                                 sleep(t);
  230.                         end;
  231.                 end;
  232.         end;
  233.         local _fun = new_fun(func,init,time,end_func,...)
  234.         return function(arg)
  235.                 if arg=='start' and flag~=true then
  236.                         cor=run(_fun);
  237.                         flag = true
  238.                 elseif arg=='end' then
  239.                         list:delete(cor);   
  240.                         flag = false;
  241.                         return _fun(0);
  242.                 elseif arg=="get" then
  243.                         return _fun(1);
  244.                 elseif arg=="status" then
  245.                         return flag;
  246.                 end;
  247.         end
  248. end

  249. --自旋方法 end ---
  250. -- 插排队列 --------------------------
  251. List = {root = nil,size = 0};
  252. -----
  253. function List:new(o,root,size)--新建对象
  254.         o =  o or {}
  255.         setmetatable(o,self)
  256.         self.__index=self
  257.         o.root = root
  258.         o.size = size
  259.         return o;
  260. end
  261. function List:add(key,value)--存入键值对
  262.         local node = Node:new(key,value)
  263.         self:insert(node);
  264. end
  265. function List:get()--通过当前时间获取值
  266.         if self.root==nil then return nil end
  267.         local node = self.root;
  268.         if node.key-GetRunningTime()<poll_step then
  269.                 self.root=node.next_node;
  270.                 self.size = self.size-1;
  271.                 return node.value;
  272.         end
  273.         return nil;
  274. end
  275. function List:empty()self.root = nil;end--清空队列
  276. function List:length()return self.size;end--获得集合长度
  277. ---------private--
  278. Node = {key = nil,value=nil,next_node=nil};
  279. function Node:new (key,value,next_node,o)
  280.         o = o or {}
  281.         setmetatable(o,self)
  282.         self.__index=self
  283.         o.key = key
  284.         o.value  = value
  285.         o.next_node = next_node
  286.         return o;
  287. end
  288. function List:insert(node)--插入调整
  289.         self.size=self.size+1
  290.         local tmp_node;
  291.         if self.root==nil then
  292.                 self.root=node ;
  293.                 return;
  294.         elseif self.root.key >=node.key then
  295.                 node.next_node=self.root
  296.                 self.root=node
  297.                 return;
  298.         else
  299.                 tmp_node=self.root;
  300.         end
  301.         while tmp_node.next_node~=nil do
  302.                 if tmp_node.next_node.key>=node.key then
  303.                         node.next_node=tmp_node.next_node;
  304.                         tmp_node.next_node=node;
  305.                         return;
  306.                 else
  307.                         tmp_node=tmp_node.next_node;
  308.                 end
  309.         end
  310.         tmp_node.next_node=node
  311. end
  312. function List:delete(value)--删除调整
  313.         if self.root==nil then
  314.                 return false;
  315.         elseif self.root.value==value then
  316.                 self.root=self.root.next_node;
  317.                 return true;
  318.         end
  319.         local tmp_node = self.root;
  320.         while tmp_node.next_node~=nil do
  321.                 if tmp_node.next_node.value==value then
  322.                         tmp_node.next_node=tmp_node.next_node.next_node;
  323.                         return true;
  324.                 end
  325.                 tmp_node=tmp_node.next_node;
  326.         end
  327.         return false;
  328. end

  329. -- 插排队列 end--------------------------

复制代码
发表于 2019-9-27 06:46:13 |显示全部楼层 来自:浙江
您需要登录后才可以回帖 登录 | 注册网易通行证