Timer 定时器


#1

参照官方手册,可利用Timer实现定时输出功能

Fnum = 200
disp(timer) = (global Fnum += 1; println(Fnum))
t = Timer(disp, 2, interval = 1)
wait(t)

程序运行后会首先暂停2秒,然后以1秒为间隔输出Fnum每次“+1”的结果,需要停止输出时在REPL中输入

close(t)

但是以上定时输出模式在遇到诸如while,for循环时,不能实现定时输出功能:

Fnum = 200
disp(timer) = (global Fnum += 1; println(Fnum))
t = Timer(disp, 2, interval = 1)
wait(t)

i = 0
A = 1.0
while i<10
    global A = 2.0
end

上述的死循环过程中REPL在输出“201”之后不会再输出任何信息,不能实现定时输出功能,不知大佬们是否有解决方法,非常感谢。


#2

这里执行到while的时候,会把主线程block住了

你可以试试julia1.3的多线程? 把后面那个死循环放在一个task里,然后设置sticky=false

$ JULIA_NUM_THREADS=8 julia

my_task = @task begin
# do sth
end

my_task.sticky = false

schedule(my_task)

噢,对了,Threads.@spawn 默认就是sticky=false


似乎即使使用了sticky=false,也不一定会放在另外一个线程(之前在英文discourse上有看Jeff提到,以后应该会增加个API?找不到那个链接了)


#3

谢谢您的帮助,采用@task协程并行似乎不能解决问题,不过进程并行似乎能够达到效果,实现代码如下:

using Distributed
addprocs(1)#增加1个worker,编号为2
Fnum = 0
i = 0
A = 0
disp(timer) = (global Fnum += 1; println(Fnum, ", ", A))
t = Timer(disp, 0, interval = 1)
wait(t)

#循环放置到2号worker上执行
A = fetch(@spawn begin
        while i < 10
                global A += 1
                if A > 1e5
                        global i = 10
                end
        end
        return A
end)

定时输出放在主进程上,每次输出“Fnum, A”两个数字。程序运行后定时输出可正常进行,为检测whlie循环是否正常工作,循环采用条件退出,当A累加到1e5时,循环终止,并返回A,此时可见定时输出从“Fnum, 0”转变为“Fnum, 100001”,证明while循环工作正常。
在重复多个while循环的条件下,可实现定时输出功能。
如果将While循环的条件放到主进程,中间循环计算部分放到2号进程,可定时输出循环中的变量。


#4

嗯,是的,我后来补充了。Threads.@spawn并不能保证一定会运行在其它线程上,我之前测试的结果也确实如此。


#5

Julia的Task是对称协程,如果你在等待IO或者某个事件,可以把CPU时间交出去,但是对于你这样的CPU密集计算就没用了,你需要的是多个可并发的控制流,考虑多线程或者多进程。

julia> Timer(1) do _
           println("A")
       end
Timer(Ptr{Nothing} @0x000000002eebd860, Base.GenericCondition{Base.Threads.SpinLock}(Base.InvasiveLinkedList{Task}(Task (runnable) @0x00000000102eb3d0, Task (runnable) @0x00000000102eb3d0), Base.Threads.SpinLock(Base.Threads.Atomic{Int64}(0))), true)

julia> sleep(2);println("B")
A
B

julia>

(请在一秒钟内敲完两条命令)

顺便一提,如果不使用中断或类似等价物,你甚至无法在一个线程忙于计算的时候通知它定时器到时间了。举个例子,Windows系统的API有两类定时器,一类是走Windows消息WM_TIMER的,它在应用程序的消息循环内分发该消息到你的窗口或者调用回调函数,本质上是轮询式的;另一类定时器内部是线程池,触发定时器之后会唤醒一个工作线程来运行你的回调函数。


#6

谢谢您的回复, 现在采用的是进程并行。目前官方好像不推荐使用线程并行,后期可以试一下。