一、改变goocanvas线条自动画线时间间隔
通过系统SIGALRM信号触发,每秒画一条线对于慢温湿度等慢变信号可以应付,但对于快速信号1秒的间隔就太慢了。可以改变方式,通过另外的线程,完成要做的任务。
1. 线程的回调函数 myfunc
2. 循环运行的线程函数 threadtimer
3. 创建线程,无需等待线程结束
4. 进入主程循环
#include once "glib.bi"
'Thread callback
Function myfunc Cdecl (Byval NotUsed As Any Ptr) as guint
Print "Main-program time = ", Time$
Return TRUE
End Function
'Thread endless loop
Function threadtimer(ByVal invalue as Integer) as integer
Do while inkey$<> CHR$(27)
Sleep 200
Print "Thead-process time = ", Time$
myfunc(NULL)
Loop
Return 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
Loop
End
执行结果:
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 = 0
Dim shared as any ptr maintlock, threadlock
Dim as any ptr threadid
Declare 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 uinteger
mainlock = mutexcreate( )
threadlock = mutexcreate( )
'Thread callback
Function myfunc Cdecl (Byval NotUsed As Any Ptr) as guint
Print "Main-program time = ", Time$
Return TRUE
End Function
'Thread endless loop
Function threadtimer(ByVal invalue as Integer) as integer
Do while inkey$<> CHR$(27)
Mutexlock threadlock
If FlagCounterNew <> FlagCounterOld then
FlagSet = 0
FlagCounterOld = FlagCounterNew
Else
FlagSet += 1
If FlagSet > 20 then
FlagSet = 0
Exit Do
End if
End if
MutexUnlock threadlock
Sleep 200
Print "Thead-process time = ", Time$
myfunc(NULL)
Loop
Return TRUE
End function
Sub timerevent()
MutexLock mainlock
FlagCounterNew += 1
If FlagCounterNew > 1000 then
FlagCounterNew = 0
End if
MutexUnlock mainlock
End Sub
sub 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 time
timerevent
end sub
signal(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
Loop
End
如果是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 guint
Print "Callback time = ", Time$
Return TRUE
End Function
gtk_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 guint
Print "Callback time = ", Time$
Return TRUE
End Function
Sub sidethread()
gtk_init(NULL, NULL)
var mycall = g_timeout_add (300, @myfunc, NULL)
gtk_main()
End Sub
Dim 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
End 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 guint
Print "Callback time = ", Time$
Return TRUE
End Function
Dim 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 Sub
Dim 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封装需要一些方法,但试练都不是特别复杂。