一、改变goocanvas线条自动画线时间间隔
通过系统SIGALRM信号触发,每秒画一条线对于慢温湿度等慢变信号可以应付,但对于快速信号1秒的间隔就太慢了。可以改变方式,通过另外的线程,完成要做的任务。
1. 线程的回调函数 myfunc
2. 循环运行的线程函数 threadtimer
3. 创建线程,无需等待线程结束
4. 进入主程循环
#include once "glib.bi"'Thread callback
Function myfunc Cdecl (Byval NotUsed As Any Ptr) as guintPrint "Main-program time = ", Time$Return TRUE
End Function'Thread endless loop
Function threadtimer(ByVal invalue as Integer) as integerDo while inkey$<> CHR$(27)Sleep 200Print "Thead-process time = ", Time$myfunc(NULL)LoopReturn TRUE
End function'Create thread
Dim as any ptr threadid = threadcreate( CAST(Any Ptr, @threadtimer), 0 )'Main loop
'Press ESC to terminate
Do while inkey$<>chr$(27)Sleep 100
LoopEnd
执行结果:
FreeBasic是多线程的,根据需要还可以创建更多线程,线程循环中sleep 200改为 sleep 100 则线程任务被执行的频率就会更高。
二、让SIGALRM为线程提供安全保护
上面的线程是个死循环,如果主程序很长,任务很重等其它原因死掉了,线程应该自动退出、让出系统资源。让SIGALRM做此事情即可解决这一问题。
系统在正常运行时,SIGALRM应该是长活的,如同C#中的系统线程池,它不是由用户创建的。它可以自动更新共享变量,在线程循环中对变量更新进行判断,如果SIGALRM在更新共享变量,则清FlagSet, 否则自动累加,这样如果因程序死掉了而不能更新共享变量的话,线程应该也就因此自动结束了。
三、可以使用线程锁
如果线程读写变得复杂,可以对变量或过程读写加锁,freebasic 支持 mutexlock 和 mutexunlock。
#include once "glib.bi"const SIGALRM=14
Dim shared as Integer FlagSet = 0
Dim shared as Integer FlagCounterNew = 0
Dim shared as Integer FlagCounterOld = 0Dim shared as any ptr maintlock, threadlock
Dim as any ptr threadidDeclare Function Signal cdecl alias "signal" (ByVal V_Signal As long, byval V_Function As Any Ptr) as Any Ptr
Declare Function Raise cdecl alias "raise" (ByVal V_Signal As long) as Any Ptr
Declare function alarm cdecl alias "alarm" (byval __seconds as uinteger) as uintegermainlock = mutexcreate( )
threadlock = mutexcreate( )'Thread callback
Function myfunc Cdecl (Byval NotUsed As Any Ptr) as guintPrint "Main-program time = ", Time$Return TRUE
End Function'Thread endless loop
Function threadtimer(ByVal invalue as Integer) as integerDo while inkey$<> CHR$(27)Mutexlock threadlockIf FlagCounterNew <> FlagCounterOld thenFlagSet = 0FlagCounterOld = FlagCounterNewElseFlagSet += 1If FlagSet > 20 thenFlagSet = 0Exit DoEnd ifEnd ifMutexUnlock threadlockSleep 200Print "Thead-process time = ", Time$myfunc(NULL)LoopReturn TRUE
End functionSub timerevent()MutexLock mainlockFlagCounterNew += 1If FlagCounterNew > 1000 thenFlagCounterNew = 0End ifMutexUnlock mainlock
End Subsub wait_alarm(iSigno As Integer)'?"This is the scheduled event SIG = " & Str$(iSigno) & "--- @: " & Time$alarm(1) 'loads another alarm events: each alarm event is a single time event, so to repeat it it must be set again every timetimerevent
end subsignal(SIGALRM, @wait_alarm) 'when SIGALRM triggered, call subroutine wait_alarm
alarm(2) 'Create thread
threadid = threadcreate( CAST(Any Ptr, @threadtimer), 0 )'Main loop
'Press ESC to terminate
Do while inkey$<>chr$(27)Sleep 100
LoopEnd
如果是gtk界面程序,可以简单地使用g_timeout_add(100, @myfunc, NULL) 完成100毫秒间隔的回调函数执行,直到回调函数返回FALSE结束。
#INCLUDE once gtk/gtk.h
#DEFINE __USE_GTK3__
'Thread callback
Function myfunc Cdecl (Byval NotUsed As Any Ptr) as guint
Print "Callback time = ", Time$
Return TRUE
End Function
gtk_init(NULL, NULL)
g_timeout_add(100, @myfunc, NULL)
gtk_main()
如果还有一个回调任务要完成,则加多一行任务,比如:
g_timeout_add(100, @myfunc1, NULL)
g_timeout_add(100, @myfunc2, NULL)
可直接编译运行的实测代码:
#DEFINE __USE_GTK3__
#Include once "gtk/gtk.bi"Declare Function myfunc Cdecl (Byval NotUsed As Any Ptr) as guint'Thread callback
Function myfunc Cdecl (Byval NotUsed As Any Ptr) as guintPrint "Callback time = ", Time$Return TRUE
End Functiongtk_init(NULL, NULL)
var mycall = g_timeout_add (100, @myfunc, NULL)
gtk_main()
上面的代码测试了Timer, 但主程序只能等待gtk_main()结束才能继续。如果将gtk这段代码作为一个线程运行,而且主程序不等待线程结束的话,则线程运行的同时主程序仍然继续运行。
测试代代码改写为:
#DEFINE __USE_GTK3__
#Include once "gtk/gtk.bi"Declare Function myfunc Cdecl (Byval NotUsed As Any Ptr) as guint'Thread callback
Function myfunc Cdecl (Byval NotUsed As Any Ptr) as guintPrint "Callback time = ", Time$Return TRUE
End FunctionSub sidethread()gtk_init(NULL, NULL)var mycall = g_timeout_add (300, @myfunc, NULL)gtk_main()
End SubDim as any ptr threadid = threadcreate( CAST(Any Ptr, @sidethread), 0 )''Main loop
Do while inkey$<>chr$(27)Print "Main program time = ", Time$Sleep 100
LoopEnd 0
Gnu 对 glib 描述有一段文字(): Libraries may contain wrappers of some of these functions, e.g. gtk_main()
, gtk_main_quit()
and gtk_events_pending()
.
是说gtk_main(), gtk_main_quit,gtk_events_pending() 是 g_main_xxx 的封装 (wrapper) --- (Gnu 站点链接贴图本意只是为了不在CSDN外链)。上面的测试程序引用了 gtk,下面的测试程序使用glib验证了同样的效果。
#Include once "glib.bi"Declare Function myfunc Cdecl (Byval NotUsed As Any Ptr) as guint'Thread callback
Function myfunc Cdecl (Byval NotUsed As Any Ptr) as guintPrint "Callback time = ", Time$Return TRUE
End FunctionDim shared as GMainLoop PTR gml = NULL
Sub sidethread()gml = g_main_loop_new(NULL, FALSE)var mycall = g_timeout_add (300, @myfunc, NULL)g_main_loop_run(gml)g_main_loop_unref(gml)
End SubDim as any ptr threadid = threadcreate( CAST(Any Ptr, @sidethread), 0 )''Main loop
Do while inkey$<>chr$(27)Print "Main program time = ", Time$Sleep 100
Loop'When main program terminate, all thread should be removed off
'but, anyway, g_main_loop_quit(gml) is not harm to added here
g_main_loop_quit(gml)
End 0
在CSDN上又查看了些关于Glib的博客,补了一句:g_main_loop_unref(gml)
学习参考博主李尔阳博文:七、GLib主要的事件循环(The Main Event Loop)_glib 事件源 套接字-CSDN博客
凭籍多线程能力,可以在主程序中实现对其它语言封装的.so界面进行调用。gcc的.so封装和freebasic的.so封装需要一些方法,但试练都不是特别复杂。