快手内核库 多线程函数
1.创建线程基础函数
thread.create("线程函数","线程函数参数" ) = 创建线程,返回句柄、线程ID;第一个参数是启动线程的函数,其余参数传递给线程函数;注意线程函数必须使用独立的闭包,import等语句应置于函数内部。
线程句柄不再使用时,应调用raw.closehandle("线程句柄“)函数关闭。。
*
thread.create(.(aau文件路径,调用参数) = 创建线程运行 *.aau 文件,该文件所在目录将被设定为此线程的应用程序根目录,该目录下的"\lib\"目录将被设定为用户库目录.
thread.create_suspended=true —— 设置创建线程时是否暂停执行(
必须在创建线程之前才有效)
2.线程加锁解锁
thread.lock("临界区名称") = 加锁
用以避免多个线程同时执行加锁范围内的代码。
thread.unlock("临界区名称") = 解锁
thread.lock() = 加锁, 等效于thread.lock("default")
thread.unlock() = 解锁, 等效于thread.unlock("default")
3.线程暂停继续执行
thread.resume(*线程句柄*) = 继续执行
thread.suspend(*线程句柄*) = 暂停执行,注意线程是否正在使用互斥锁
4.终止线程
thread.stop(*退出代码*) = 终止当前线程。
thread.terminate(*线程句柄*,0) = 强制终制线程,不推荐使用, 参数二设定返回值,可通过thread.getExitCode获取。
5.线程操作
thread.call("__/*请输入函数名字*/","owner", ) = 执行线程共享区函数
thread.getExitCode(__/*线程句柄*/) = 获取线程函数返回代码 (返回线程的退出返回值)
thread.getMainId() = 返回主前线程ID
thread.getId() = 返回当前线程ID
6.线程储存
thread.set("标志键", ) = 写入一个值到进程共享内存区
标志键可以是数值、字符串等常量、赋值为null则删除该共享键值对此函数禁止在任何元方法中使用。
thread.get("标志键") = 从进程共享内存区读取一个值
标志键可以是数值、字符串等常量, 此函数禁止在任何元方法中使用。
thread.tostdcall(.(函数对象,"void()") = 创建跨线程回调函数,使用stdcall调用约定,回调线程不是当前线程应使用此函数替换raw.tostdcall,回调函数使用独立的线程环境,所有import语句必须放在函数体内部
thread.tocdecl(.(函数对象,"void()") = 创建跨线程回调函数,使用cdecl调用约定,回调线程不是当前线程应使用此函数替换raw.tocdecl,回调函数使用独立的线程环境,所有import语句必须放在函数体内部
7.线程拓展
thread.waitAll = function(...){
return threadwait(1,...);
}
thread.waitOne = function(...){
return threadwait(0,...);
}
thread.wait = function(handle,ms=0xFFFFFFFF){
return WaitForSingleObject(handle,ms);
}
thread.wait(”线程句柄“,"超时时间") //等待一个线程执行完成
thread.waitOne( "线程句柄 ","等待超时时间") //等待其中任意一个线程执行完成
thread.waitAll( "线程句柄" ,"超时时间") //等待所有线程执行完成
上面指的多个线程句柄可以是多个参数,也可以是包含多个线程句柄的table数组。
thread.setAffinity(__,2) = 指定线程在哪个CPU上运行
thread.setAffinity(__) = 指定线程在CPU 1上运行
线程共享表(thread.table)
thread.table = 线程共享表
thread.table("table_name") = 创建多线程共享table
thread.table() = thread_table.
?thread.table = thread_table.
thread_table.get("键名") = 读取成员值
thread_table.set("键名",__/*值*/) = 写入成员值
thread_table.getByNamespace("__/*指定表内名字空间路径*/") = 在指定表查找并返回成员
thread_table.setByNamespace("__/*指定表内名字空间路径*/",新值) = 参数一指定表内名字空间路径,如果不存在则则创建此表内名字空间,\n支持索引操作符,可以使用[]空索引实现在数组尾部添加值\n参数二指定需要设置的值,如果不指定则创建空表并赋值
thread_table.lenByNamespace("__/*指定表内名字空间路径*/") = 返回指定元素长度
thread_table.next(.() = 获取table的第一个键值对元素
thread_table.next(.( key ) = 迭代获取table的下一个键值对元素
thread_table.tostring(__) = 获取table的字符串表达式
thread_table.concat(.( tab2 ) = 拼接多个table对象
thread_table.insert(.(要插入的值,要插入的位置 ) = 插入元素到指定位置
thread_table.insert(.(要插入的值 ) = 插入元素到table头部
thread_table.remove(.(位置 ) = 在表中指定位置移除元素
thread_table.remove(.( ) = 在table头部移除元素
thread_table.push(.(__) = 在顺序数组尾部压入一个或多个元素,返回数组大小
thread_table.push(.(__v1,v2,v3,...) = 在顺序数组尾部压入多个元素,返回数组大小
thread_table.pop(__) = 在顺序数组尾部弹出一个或多个元素并作为返回值
thread_table.sort(.( ) = 排序
thread_table.sort(.( comProc ) = 排序,comProc指定用于比较元素大小的函数
thread_table.reverse(__) = table数组倒序排列。
thread_table.range(__) = 返回table数组的最小索引,最大索引\nmin,max=tab.range();
thread_table.unpack(__) = 返回表中所有的元素\na,b,c,d,e = tab.unpack();
thread_table.left(len__) = 返回table对象左侧开始返回指定个数的元素。
thread_table.right(len__ = 返回table对象右侧开始返回指定个数的元素
thread_table.len() = 返回table长度
thread_table.count() = 获取table成员总数
thread_table.each = @for( k,v in ??.each() ){
__
}
快手拓展库 多线程函数
1。线程命令 (thread.command库)
响应式编程,也可以叫命令式编程,也就是说每个线程都可以发送命令,每个线程也都可以接收命令,发送命令的人不关心你怎么处理,接收命令的人不关心数据是怎么来的,这样就实现了逻辑解耦,线程的职责也就更清晰。
UI线程用 thread.command 响应命令操作控件
thread.command.post(.(窗口句柄,"命令函数名",其他参数) = 不阻塞调用跨线程命令
thread.command.post(.("命令函数名",其他参数) = 不阻塞调用跨线程命令,\n不指定窗口句柄,所有同名的线程命令函数都会被调用
thread.command.send(.(窗口句柄,"命令函数名",其他参数) = 阻塞调用跨线程命令,\n可获取回调函数的数值返回值
thread.command.send(.("命令函数名",其他参数) = 阻塞调用跨线程命令\n不指定窗口句柄,所有同名的线程命令函数都会被调用,\n可获取最后一个回调函数的数值返回值
thread.command(.(窗口对象) = 创建线程命令对象\n省略参数则创建 message only window,\n该对象定义的成员函数,都可在其他线程中用 thread.command.post()调用
thread.command() = !thread_command.
!thread_command.自定义函数名 = @.命令函数 = function(__/*支持不定个数参数*/){
}
!thread_command._form = 窗体对象\n!winform.
_WM_THREAD_COMMAND=@0x4CC/*_WM_THREAD_COMMAND*/
示例1
-
import win.ui;
-
/*DSG{{*/
-
var winform = ..win.form( right=599;text="多线程命令";bottom=399 )
-
winform.add(
-
edit={ bottom=269;right=465;left=54;multiline=1;top=51;z=1;edge=1;cls="edit" }
-
)
-
/*}}*/
-
-
import thread.command;
-
临听器 = thread.command()
-
临听器.发现了 = function(idx){
-
winform.edit.appendText("发现了:",idx,'\r\n')
-
}
-
-
thread.create(
-
function(num){
-
import thread.command;
-
for(i=1;num;1){
-
//调用其他线程的命令
-
thread.command.post("发现了",i)
-
}
-
},20//给线程函数传参
-
)
-
-
winform.show()
-
win.loopMessage();
示例2 线程命令订阅模式
-
//线程命令订阅模式
-
import win.ui;
-
/*DSG{{*/
-
var winform = ..win.form( bottom=399;parent=...;right=599;text="AAuto Form" )
-
winform.add(
-
edit={ bottom=273;right=521;left=66;multiline=1;top=52;z=1;edge=1;cls="edit" }
-
)
-
/*}}*/
-
-
import thread.command;
-
var 订阅者 = thread.command();
-
订阅者.自定义线程命令 = function(...){
-
winform.edit.appendText('\n',...) //将参数追加输出到文本框
-
}
-
订阅者.自定义线程命令2 = function(...){
-
winform.edit.appendText('\n',...) //将参数追加输出到文本框
-
}
-
-
//创建线程
-
thread.create(
-
function(hwnd){
-
-
import thread.command
-
-
//如果不指定窗口,所有订阅了此命令的窗口相应函数都会被调用
-
thread.command.post("自定义线程命令","线程调用",",线程调用参数2",'\r\n')
-
-
//也可以显式指定窗口,仅触发指定窗口的命令
-
thread.command.post(hwnd,"自定义线程命令2","线程调用",'\r\n')
-
-
},订阅者._form.hwnd
-
)
-
-
winform.show()
-
-
win.loopMessage();
示例3 多线程采集网址
-
import win.ui;
-
/*DSG{{*/
-
var winform = ..win.form( bottom=400;parent=...;right=600;text="AAuto Form";scroll=1 )
-
winform.add(
-
listview={ bgcolor=16777215;bottom=354;right=527;left=26;top=24;z=1;edge=1;cls="listview" }
-
)
-
/*}}*/
-
-
winform.show();
-
-
//列标题
-
winform.listview.insertColumn("id",30,1);
-
winform.listview.insertColumn("url",300,2);
-
-
import thread.command;
-
var 订阅者 = thread.command( winform );
-
订阅者.send = function(str){
-
var i=winform.listview.addItem();
-
winform.listview.setItemText(tostring(i),i,1);
-
winform.listview.setItemText(str,i,2);
-
}
-
-
var gjz="aauto";
-
for(i=1;10;1){
-
var url=""+gjz+"&usm=1&pn="+i*10;
-
thread.create(function(url){
-
import thread.command;
-
import inet.whttp;
-
var http=inet.whttp();
-
var html=http.get(url);
-
if(html){
-
for url in string.gmatch(html,'span class=\"g\"\>(.*?)\/'){
-
thread.command.post("send",url);
-
}
-
}
-
},url)
-
win.delay(1000);
-
}
-
-
win.loopMessage();
-
return winform,wb;
2。事件对象(thread.event) ——— 处理主线程与子线程同步
thread.event = 事件同步对象支持库,可跨进程、跨线程使用.
thread.event("事件对象名称","默认自动置位”,“事件未触发”) = 创建或打开已存在的命名事件对象;参数2是否手动复原信号状态,初始状态0,默认自动复原;参数3为事件触发状态,默认未触发)
参数( 名称,是否手动复原信号状态,初始状态0) 参数2, 参数3 是可选参数,仅在创建新事件对象是起作用.
thread_event.close() = 关闭事件对象
该函数并不关闭信号量内核对象
当所有引用内核对象的对象关闭,内核对象自动释放
如果没有手工调用此函数,则线程结束时自动调用.
thread_event.conflict = 如果事件对象已存在,此属性为真值
否则为空值
thread_event.set() = 触发事件,设置事件的状态为有信号状态(每次触发后,必有一个或多个处于等待状态下的线程变成可调度状态),
退出所有等待该事件对象信号的函数
thread_event.reset() = 将事件设为末触发,事件对象设置为无信号状态,
如果创建事件对象时并未设定手动复位,此函数不需要手动调用.
thread_event.pulse() = 设置事件对象为有信号状态,并释放某些等待线程
然后设置该事件无效(无信号状态)
thread_event.wait() = 等待事件到有信号状态,
可选增加一个参数指定超时,以毫秒为单位
在UI线程中应使用非阻塞的waitOne()函数替代
thread_event.waitOne() = 等待事件到有信号状态,
可选增加一个参数指定超时,以毫秒为单位
示例1
-
//使用事件对象同步
-
-
import thread.event;
-
evt = thread.event("事件对象名称 GUID:32DA0FC7-A96D-4850-9A28-DA1DD4464B44")
-
-
io.open()
-
thread.create(
-
function(){
-
import thread.event;
-
evt = thread.event("事件对象名称 GUID:32DA0FC7-A96D-4850-9A28-DA1DD4464B44")
-
-
io.print("工作线程:","不客气,您先吧")
-
sleep(1000)
-
evt.set()
-
evt.wait()
-
-
..io.print("工作线程:您可真啰嗦")
-
sleep(1000)
-
evt.set()
-
evt.wait()
-
-
..io.print("工作线程:无语,很无语............")
-
sleep(1000)
-
evt.set()
-
}
-
)
-
-
-
io.print("主线程: 您先请")
-
thread.wait( evt ) //也可以作为thread.wait()的参数使用
-
io.print("主线程:那我就不客气了......#_#~!@")
-
-
io.print("主线程:还是您先吧")
-
evt.set()
-
evt.wait();
-
-
io.print("主线程:小子欠抽是吧?...............")
-
evt.set()
-
evt.wait();
-
-
io.print("完了完了............")
示例2
-
//定时执行线程任务
-
-
import win.ui;
-
/*DSG{{*/
-
var winform = win.form(parent=...; text="使用thread.event创建定时执行任务的线程";right=349;bottom=211 )
-
winform.add(
-
trackbar={ bottom=82;max=1000;text="trackbar";left=15;top=52;z=2;right=319;min=500;cls="trackbar" };
-
lbTip={ bottom=103;text="";left=26;right=100;top=85;z=3;transparent=1;cls="static" };
-
static={ bottom=44;align="center";text="";left=138;top=13;center=1;z=5;right=309;edge=1;cls="static" };
-
btnStart={ bottom=155;text="启动定时线程";left=61;top=113;z=1;right=181;cls="button" };
-
btnStop={ disabled=1;bottom=155;right=312;left=192;top=113;z=4;text="结束定时线程";cls="button" }
-
)
-
/*}}*/
-
-
task_t = function(hwnd,ms){
-
import win;
-
import thread.event;
-
-
var evt = thread.event("定时事件 GUID:32DA0FC7-A96D-4850-9A28-DA1DD4464B44")
-
while( ! evt.wait(ms) ){
-
win.setText(hwnd,tostring( time() ) )
-
}
-
io.print("任务已完成")
-
}
-
-
import thread.event;
-
var evtTask = thread.event("定时事件 GUID:32DA0FC7-A96D-4850-9A28-DA1DD4464B44",false)
-
-
winform.btnStart.oncommand = function(id,event){
-
-
winform.btnStart.disabled = true;
-
winform.btnStop.disabled = false;
-
winform.trackbar.disabled = true;
-
-
hThread = thread.create(task_t,winform.static.hwnd,winform.trackbar.pos);
-
thread.waitOne(hThread)
-
-
winform.btnStart.disabled = false;
-
winform.btnStop.disabled = true;
-
winform.trackbar.disabled = false;
-
}
-
-
winform.btnStop.oncommand = function(id,event){
-
evtTask.set();//使事件对象切换为有信号状态,使wait函数退出
-
}
-
-
winform.trackbar.oncommand = function(id,event,pos){
-
winform.lbTip.text = owner.pos + "毫秒"
-
}
-
-
winform.show()
-
win.loopMessage();
3。线程互斥量(process.mutex) ——— 确保一个线程独占一个资源的访问。并且互斥量可以用于不同进程中的线程互斥访问资源。
互斥体有一点象thread.lock()创建的线程临界锁, 不同的是他可以跨进程互斥.
互斥体虽然能象原子窗体那样限制应用程序仅创建一个实例,但是他不能自动激活原来的窗体, 所以一般推荐使用原子窗体.
process.mutex("互斥体唯一名称") = @mutex=process.mutex("__/*输入唯一标识字符串\n建议使用GUID生成Globally Unique Identifier(全球唯一标识符) */")\nif( mutex.conflict ){\n io.print("互斥体已存在",mutex.handle)\n}\nmutex.close();
mutex.close()=关闭互斥体句柄\n该函数并不关闭互斥体创建的内核对象\n当所有引用内核对象的对象关闭,内核对象自动释放
mutex.conflict = 如果互斥体已存在,此属性为真值\n否则为空值
mutex.release() = 线程在处理完共享资源后,\n应在离开时调用此函数释放互斥体所有权
mutex.wait() = 等待并获取互斥体独占所有权,\n可选增加一个参数指定超时,以毫秒为单位\n注意此函数应与release()函数配对使用\n在UI线程中应使用非阻塞的waitOne()函数替代
mutex.waitOne() = 等待并获取互斥体独占所有权,\n可选增加一个参数指定超时,以毫秒为单位\n注意此函数应与release()函数配对使用
示例1 限制开启程序个数
-
import win.ui;
-
import process.mutex
-
/*DSG{{*/
-
var winform = win.form(parent=...; text="AAuto Form";right=349;bottom=249 )
-
winform.add( )
-
/*}}*/
-
-
//创建互斥体
-
mutex=process.mutex("建议创建一个GUID作为唯一标识")
-
if( mutex.conflict ){
-
winform.msgbox("该程序仅允许运行一个实例" )
-
mutex.close()
-
return;
-
}
-
-
winform.show()
-
win.loopMessage();
-
-
//程序退出再释放互斥体
-
mutex.close();
示例2 实现进程退出后再运行别的进程文件
代码复制到工程 main.aau 中生成EXE后再运行测试下例
-
import win.ui;
-
/*DSG{{*/
-
mainForm = ..win.form( right=600;bottom=400;text="test mutex" )
-
mainForm.add( )
-
/*}}*/
-
-
mainForm.show()
-
-
import process.mutex;
-
mutex = process.mutex("test mutex")
-
-
if( mutex.conflict ) {
-
-
mainForm.text = "正在等待前面的进程退出"
-
mutex.waitOne()
-
-
mainForm.text = "xx进程已退出"
-
}
-
else {
-
import process;
-
import fsys;
-
-
//复制自己到临时目录
-
var tempPath = fsys.joinpath( fsys.getTempDir(),io._exefile );
-
fsys.copy( io._exepath, tempPath );
-
-
process.execute(tempPath);
-
mainForm.text = "请关闭程序"
-
}
-
-
win.loopMessage();
-
mutex.close();
4。信号量(thread.semaphore) ——— 在经典多线程问题中设置一个信号量和一个关键段。用信号量处理主线程与子线程的同步,用关键段来处理各子线程间的互斥。
thread.semaphore("信号量对象名称__",10) = 创建信号量同步对象 参数3是可选参数,默认值等于最大资源计数; 参数2,参数3仅在首次创建该事件对象是起作用.
参数( 名称,最大资源计数,初始空闲资源计数0)
thread_semaphore.close() = 关闭信号量对象\n该函数并不关闭信号量内核对象\n当所有引用内核对象的对象关闭,内核对象自动释放
thread_semaphore.conflict = 如果信号量对象已存在,此属性为真值\n否则为空值
thread_semaphore.release() = 线程在处理完共享资源后,\n应在离开时调用此函数将可用资源计数加1\n可在参数中指定要释放的资源计数(默认为1)\n第一个返回值表示函数执行是否成功,\n第二个返回值为原来的资源计数
thread_semaphore.wait() = 等待可用资源计数大于0,\n可选增加一个参数指定超时,以毫秒为单位\n注意此函数应与release()函数配对使用\n在UI线程中应使用非阻塞的waitOne()函数替代
thread_semaphore.waitOne() = 等待可用资源计数大于0,\n可选增加一个参数指定超时,以毫秒为单位\n注意此函数应与release()函数配对使用
thread.semaphore = 信号量同步对象支持库\n可跨进程、跨线程使用
示例
-
//计数信号量同步演示
-
import thread.semaphore
-
semaphore = thread.semaphore("事件对象名称",2);
-
-
func_t = function(){
-
import thread.semaphore
-
semaphore = thread.semaphore("事件对象名称",2);
-
-
io.print('我在等待机会') sleep(1)
-
semaphore.wait();
-
io.print("机会来了,我正在占用资源......")
-
sleep(2000)
-
semaphore.release()
-
-
io.print("已释放资源......") sleep(1)
-
}
-
-
io.open()
-
io.print("机会只有两个,但是你们都想要.......")
-
io.print("---------------")
-
-
import thread.manage
-
manage = thread.manage()
-
-
for(i=1;10;1){
-
manage.create( func_t )
-
sleep(100)
-
}
-
-
manage.waitClose();
-
io.print("---------------")
-
io.print("万里长征终于走完了.......")
//线程库扩展模块;
::WaitForSingleObject := ::Kernel32.api("WaitForSingleObject", "INT(pointer hHandle,INT dwMilliseconds)");
::WaitForMultipleObjects := Kernel32.api("WaitForMultipleObjects", "INT(INT nCount,struct lpHandles,INT bWaitAll,INT dwMilliseconds)");
var threadwait = function(bAll, ...){
var threads = ...;
if(type(threads)!=type.table)
threads ={...}
var threads_c = raw.toarray( threads ,"pointer" ,"array")
var re = WaitForMultipleObjects(#threads,threads_c,bAll, 0xFFFFFFFF /* Infinite timeout*/);
select(re) {
case 258 //WAIT_TIMEOUT
return null,"超时"
case 0xFFFFFFFF
return null,"失败"
}
return re+1;
}
thread.waitAll = function(...){
return threadwait(1,...);
}
thread.waitOne = function(...){
return threadwait(0,...);
}
thread.wait = function(handle,ms=0xFFFFFFFF){
return WaitForSingleObject(handle,ms);
}
var SetThreadAffinityMask = Kernel32.api("SetThreadAffinityMask","INT( int hThread,INT dwProcessAffinityMask)" )
thread.setAffinity = function( h,cpu = 1){
SetThreadAffinityMask( h,cpu );
}
thread.setAffinity(__,2) = 指定线程在哪个CPU上运行
thread.setAffinity(__) = 指定线程在CPU 1上运行
例子:
io.open(); //io.open建议不要在子线程中调用
io.print("多线程演示程序")
f = function(arg){
import win; //线程函数内部要添加自已的import语句
for(i=1;10;1){
io.print("线程ID:" + thread.getId() );
io.print("收到的参数:" + arg.str );
thread.lock("临界区名称");
try{ //预防抛出异常时 thread.unlock 无法执行
//加锁范围内的代码能避免多线程重入
var test = thread.get("标志名称")
test++;
thread.set("标志名称", test);
io.print( thread.get("标志名称") );
}
thread.unlock("临界区名称")
sleep(1000)
}
}
thread.set("标志名称", 1); //多线程需要通过此函数交换变量
hander,id = thread.create(f,{int=123;str="你好"} )
hander2,id2 = thread.create(f,{int=456;str="大家好"} )
io.print("thread.waitAll开始等待所有线程")
thread.waitAll(hander,hander2);
io.print("thread.waitAll函数返回")
例子:线程共享区CALL.aau
commonThreadFunc = function(a,b){
io.print("参数",owner,a,b)
io.print("线程变量",threadvar)
return 1,2,a + b
}
//将一个函数写入线程共享区(通常应当在主线程中一次性写入)
thread.set("threadfUNC", commonThreadFunc);
thread.set("threadvar", 123);
io.open()
//调用线程共享区函数,用法类似call函数
//第一个参数以字符串形式指定函数名字,第二个参数显式指定owner参数,后面可以是任意多个其他参数
io.print( thread.call("threadfUNC","owner",3,11) ) //在控制台输出所有返回值。
多线程之间发送接收消息的范例
import win;
//创建线程
th,tid = thread.create(
function(){
//线程代码
import win;
io.open()
io.print("我是创建的线程,我在等待消息")
msgproc = function(msg){
io.print("收到消息", msg.message )
}
//启动消息循环,并注册观察者
win.loopMessage(msgproc)
io.print("线程退出")
}
)
win.delay(1000)
//发送线程消息
::PostThreadMessage(tid,123,0,0);
//发送退出消息
::PostThreadMessage(tid,0x12/*_WM_QUIT*/,0,0)
这个实例很有趣。好像只有传int?要传str呢?
thread.set 是干什么的?
峰回路转,又回来了。
我定义了一个自定义函数,以及创建了一个线程,想在线程里调用 winform 代码中的自定义函数。不过线程好像跟 winform 不是一个 namespace,用 import winform 之类的也不行,不知道能否直接调用?
还是要自定义一个消息,用 SendMessage,然后窗口再去响应?
用 SendMessage 实现了,效果还不错
窗体是基于消息机制的,用消息是不错的方法,而且不需要考虑线程同步(消息可以在单线程中实现异步)。只要在创建线程的时候把窗体句柄传进去就可以了,反过来也可以给线程发消息,用 ::PostThreadMessage
线程之间用thread.get 、thread.set读写变量,会自动处理同步的问题。
阅读(3447) | 评论(2) | 转发(0) |