Vrep线程之间的切换

在使用vrep的thread scirpt的时候我竟然会对这么简单的一个问题产生疑惑,但是为了要多学习,多进步,还是把这个问题记录一下:

一个文件里有许多的函数。一个函数执行完了,会执行到另外一个函数。在执行某个函数的时候,中间也会跑出去执行别的函数,这怎么用线程的概念来解释它?  这个问题可以用时间片轮转来解释:

在单核cpu中程序是片段执行,程序员就必须理解,在自己的程序运行时不是独一无二的,我们看似很顺畅的工作,其实是由一个个的执行片段构成的,我们眼中相邻的两条语句甚至同一个语句中两个不同的运算符之间,都有可能插入其他线程或进程的动作。具体查看时间片轮转调度


   在vrep thread scirpt中,每隔2ms就会自动切换到另外一个线程,2ms之后在切换回来。sim.setThreadAutomaticSwitch()、sim.switchthread()等函数就是用来控制这个过程的。sim.setThreadAutomaticSwitch()能够禁止自动切换。sim.switchthread()指明在哪个时刻从一个线程切换到另外一个线程。我想这可能和vrep的thread scirpt管理机制有关,毕竟一个scene里可以有好几个的thread scirpt,每一个thread scirpt都是需要执行的。

接下来稍微具体的介绍一下这个过程。

1.线程和协程的概念

   (1)线程

  首先复习一下多线程。我们都知道线程——Thread。每一个线程都代表一个执行序列。当我们在程序中创建多线程的时候,看起来,同一时刻多个线程是同时执行的,不过实质上多个线程是并发的,因为只有一个CPU,所以实质上同一个时刻只有一个线程在执行。在一个时间片内执行哪个线程是不确定的,我们可以控制线程的优先级,不过真正的线程调度由CPU的调度决定。

   (2)协程

  那什么是协程呢?协程跟线程都代表一个执行序列。不同的是,协程把线程中不确定的地方尽可能的去掉,执行序列间的切换不再由CPU隐藏的进行,而是由程序显式的进行。

  所以,使用协程实现并发,需要多个协程彼此协作

Lua的多线程并不是真的多线程,而是协程——Lua的线程和状态Lua中的协同程序

协同程序与线程差不多,也就是一条执行序列,拥有自己独立的栈、局部变量和指令指针,同时又与其它协同程序共享全局变量和其它大部分东西。从概念上讲,线程与协同程序的主要区别在于,一个具有多个线程的程序可以同时运行几个线程,而协同程序却需要彼此协作的运行。就是说,一个具有多个协同程序的程序在任意时刻只能运行一个协同程序,并且正在运行的协同程序只会在其显式地要求挂起时,它的执行才会暂停。[1]

Lua不支持真正的多线程,而是一种协作式的多线程,彼此之间协作完成,并不是抢占完成任务,由于这种协作式的线程,因此可以避免由不可预知的线程切换所带来的问题;另一方面,Lua的多个状态之间不共享内存,这样便为Lua中的并发操作提供了良好的基础。

      coroutine运行一系列的协作多线程。每个coroutine相当于一个thread。通过yield-resume实现在不同thread之间切换控制权。但是,跟常规的多线程不同,coroutine是非抢占式的。一个coroutine在运行的时候,不可能被其他的coroutine从外部将其挂起,只有由其本身显式地调用yield才会挂起,并交出控制权。对一些程序来说,这没有任何问题,相反,因为非抢占式的缘故,程序变得更加简单。我们不需要担心同步问题的bug,因为在threads之间的同步都是显式的。我们只需要保证在对的时刻调用yield就可以了


2.1Non-threaded child scripts 单线程脚本/非线程脚本 

单线程的程序执行时,是按照顺序执行的。The script is non-threaded. In that case, it behaves as a function that is called in each simulation step. This also means, it is inherently synchronized with the main simulation loop. 

2.2 Threaded child scripts 多线程子脚本 

每一个脚本都在一个thread里启动。

The script is threaded. In that case, you can precisely time your code to be synchronized with the main simulation loop with following few instructions:
  • sim.SetThreadAutomaticSwitch()
  • sim.SwitchThread()
You can also specify at which time the threaded script resumes, using sim.SetThreadResumeLocation() .All that functionality is allowed because threaded scripts in V-REP behave more like coroutines 


simSetThreadResumeLocation / sim.setThreadResumeLocation 可以决定位置和脚本的执行顺序

simSetThreadSwitchTiming / sim.setThreadSwitchTiming 指定当前线程在切换到另外一个线程的之前,此线程执行多长时间

a threaded child script is running more like a coroutine than a thraditional thread:

By default, when a threaded child script is launched, it will execute about 2ms. Then V-REP will interrupt or pause it, and only resume it in next simulation step (i.e. when the simulation time has become t=t+dt). It will resume for about 2ms, and be interrupted again. And resume again in next simulation step, and so on, and so forth.


代码片段1:没有线程切换函数,while loop会浪费宝贵的计算资源。需要再while loop里执行完一个2ms

function sysCall_threadmain()
-- Put some initialization code here:
sensorHandleFront=sim.getObjectHandle("DoorSensorFront")
sensorHandleBack=sim.getObjectHandle("DoorSensorBack")
motorHandle=sim.getObjectHandle("DoorMotor")
-- Here we execute the regular thread code:
while sim.getSimulationState()~=sim.simulation_advancing_abouttostop do
resF=sim.readProximitySensor(sensorHandleFront)
resB=sim.readProximitySensor(sensorHandleBack)
if ((resF>0)or(resB>0)) then
sim.setJointTargetVelocity(motorHandle,-0.2)
else
sim.setJointTargetVelocity(motorHandle,0.2)
end
-- this loop wastes precious computation time since we should only read new
-- values when the simulation time has changed (i.e. in next simulation step).
end
end
function sysCall_cleanup()
-- Put some clean-up code here:
end

默认情况下,vrep每隔两毫秒就会自动从一个线程切换到另外一个线程。然后就会在下一个simulation pass恢复执行。

sim.switchthread() 能够缩短时间,提高效率(把2ms缩短,可以和代码1进行对比 )

      V-REP uses threads to mimic(模拟) the behavior of coroutines, instead of using them traditionally, which allows for a great deal of flexibility and control: by default a threaded child script will execute for about 1-2 milliseconds before automatically switching to another thread. This default behavior can be changed with the sim.setThreadSwitchTiming or sim.setThreadAutomaticSwitchOnce the current thread was switched, it will resume execution at next simulation pass (i.e. at a time currentTime+simulationTimeStep). The thread switching is automatic (occurs after the specified time), but the sim.switchThread command allows to shorten that time when needed. Using above three commands, an excellent synchronization with the main simulation loop can be achieved. Following code (handling the automatic sliding doors from above's example) shows child script synchronization with the main simulation loop 

代码片段2:使用线程切换函数sim.setThreadAutomaticSwitch(false)、sim.switchthread()
对代码片段1进行改变,来得到更好的效果:不会浪费宝贵的计算资源而且能够和仿真同步运行

function sysCall_threadmain()
    -- Put some initialization code here:
    sim.setThreadAutomaticSwitch(false) -- disable automatic thread switches
    sensorHandleFront=sim.getObjectHandle("DoorSensorFront")
    sensorHandleBack=sim.getObjectHandle("DoorSensorBack")
    motorHandle=sim.getObjectHandle("DoorMotor")
    
    -- Here we execute the regular thread code:
    while sim.getSimulationState()~=sim.simulation_advancing_abouttostop do
        resF=sim.readProximitySensor(sensorHandleFront)
        resB=sim.readProximitySensor(sensorHandleBack)
        if ((resF>0)or(resB>0)) then
            sim.setJointTargetVelocity(motorHandle,-0.2)
        else
            sim.setJointTargetVelocity(motorHandle,0.2)
        end
        sim.switchThread() -- Explicitely switch to another thread now!
        -- from now on, above loop is executed once every time the main script is about to execute.
        -- this way you do not waste precious computation time and run synchronously.
	-- 以上的循环在main scirpt里只会执行一次。这样做不会浪费宝贵的计算资源而且能够和仿真同步运行
    end
end

3.与thread scirpt相关的常用函数

simSetThreadAutomaticSwitch / sim.setThreadAutomaticSwitch 

DescriptionAllows to temporarily forbid thread switches. If the current script doesn't run in a thread (i.e. if it runs in the application main thread), this function has no effect. By default, V-REP doesn't use "regular" threads, but something similar to hybrid threads (which behave like coroutines, but can also behave like regular threads). This allows much more flexibility and execution control of the threads. For complete control over the switching moment, see also sim.getThreadAutomaticSwitchsim.setThreadSwitchTimingsim.switchThreadsim.setThreadIsFree and sim.setThreadResumeLocation.
C synopsis     -
C parameters-
C return value-
Lua synopsisnumber result=sim.setThreadAutomaticSwitch(Boolean switchIsAutomatic)
Lua parameters
switchIsAutomatic: if true, the thread will be able to automatically switch to another thread, otherwise the switching is temporarily disabled.
Lua return values
result: 1 if the command was successful, 0 if it didn't have an effect (e.g. because the function was called from the main or application thread), or -1 in case of an error.


simSwitchThread / sim.switchThread 在某个时刻将当前线程切换到另外一个线程

DescriptionAllows specifying the exact moment at which the current thread should switch to another thread. If the current script doesn't run in a thread (i.e. if it runs in the application main thread), this function has no effect. By default, V-REP doesn't use "regular" threads, but something similar to hybrid threads (which behave like coroutines, but can also behave like regular threads). This allows much more flexibility and execution control of the threads: each thread (except for the main or application thread) has a switch timing associated, which specifies how long the thread will run before switching to other threads. By default this value is 2 millisecond, but can be modified with sim.setThreadSwitchTiming. That timing can be shortened with sim.switchThread. Use with care when calling this function from a plugin. See also the sim.setThreadAutomaticSwitchsim.setThreadResumeLocation and sim.setThreadIsFree functions.
C synopsissimInt simSwitchThread()
C parametersNone
C return value1 if the thread was switched (the current thread gave control to other threads until the next calculation pass), 0 if it was not switched (e.g. because the function was called from the main or application thread, or from a thread started by the user), or -1 in case of an error.
Lua synopsisnumber result=sim.switchThread()
Lua parametersNone
Lua return valuesresult: 1 if the thread was switched (the current thread gave control to other threads until the next calculation pass), 0 if it was not switched (e.g. because the function was called from the main or application thread), or -1 in case of an erro



simSetThreadSwitchTiming / sim.setThreadSwitchTiming指定当前线程在切换到另外一个线程的之前,此线程执行多长时间

DescriptionAllows specifying a switching time for the thread in which the current script runs. If the current script doesn't run in a thread (i.e. if it runs in the application main thread), this function has no effect. By default, V-REP doesn't use "regular" threads, but something similar to hybrid threads (which behave like coroutines, but can also behave like regular threads). This allows much more flexibility and execution control of the threads: each thread (except for the main or application thread) has a switch timing associated, which specifies how long the thread will run before switching to other threads (the execution duration per calculation pass). By default this value is 2 millisecond, but this function allows changing that value (on a thread-basis). Acceptable values are between 0 and 200. For complete control over the switching moment, see also sim.setThreadAutomaticSwitchsim.switchThreadsim.setThreadIsFree and sim.setThreadResumeLocation.
C synopsis-
C parameters-
C return value-
Lua synopsisnumber result=sim.setThreadSwitchTiming(number deltaTimeInMilliseconds)
Lua parameters
deltaTimeInMilliseconds: desired non-stop execution time before a switching occurs. A value of x will let the thread execute for x-1 to x milliseconds before switching to another thread.
Lua return values
result: 1 if the timing was set, 0 if it was not set (e.g. because the function was called from the main or application thread), or -1 in case of an error.

simResumeThreads / sim.resumeThreads 恢复某个线程

DescriptionIn conjunction with sim.setThreadResumeLocation, sim.resumeThreads allows specifying when and in which order threads are resumed. By default, V-REP doesn't use "regular" threads, but something similar to hybrid threads (which behave like coroutines, but can also behave like regular threads). This allows much more flexibility and execution control of the threads. Once a thread switched to another thread, it will resume execution at the beginning of next simulation pass by default. In order to also have full synchronization control between threads, you can assign a resume location and order to each thread. When sim.resumeThreads(x) is called, all threads that were assigned a resume location of x will be resumed. See also sim.setThreadResumeLocationsim.setThreadSwitchTimingsim.switchThread and sim.setThreadIsFree. This function can only be called in the main script.

simSetThreadResumeLocation / sim.setThreadResumeLocation  

DescriptionAllows specifying when and in which order child script threads are resumed. If the current script doesn't run in a thread (i.e. if it runs in the application main thread), this function has no effect. By default, V-REP doesn't use "regular" threads, but something similar to hybrid threads (which behave like coroutines, but can also behave like regular threads). This allows much more flexibility and execution control of the threads. Once a thread switched to another thread, it will resume execution when the main script calls sim.resumeThreads with the corresponding argument, which represents a child script thread resume location. In order to also have full synchronization control between threads, you can assign a resume location and order to each thread with this function. See also sim.setThreadSwitchTimingsim.setThreadAutomaticSwitchsim.switchThread and sim.setThreadIsFree.
C synopsis-
C parameters-
C return value-
Lua synopsisnumber result=sim.setThreadResumeLocation(number location,number order)
Lua parameters
Lua return values
result: 1 if the command was applied, 0 if it was not (e.g. because the function was called from the main or application thread), or -1 in case of an error.

4.举例说明:

创建分成以下四种情况:

4.1

function sysCall_threadmain()
    local lastSimulationTime=sim.getSimulationTime()
    local simulationStep=sim.getSimulationTimeStep()
    while true do
        local cnt=0
        -- Inner loop:
        while sim.getSimulationTime()==lastSimulationTime do
            cnt=cnt+1
        end
        printf('Inner loop executed %i times for one simulation step.',cnt)
        local dt=sim.getSimulationTime()-lastSimulationTime
        printf('Thread was interrupted %i times since last simulation step.',math.floor((dt/simulationStep)+0.5))
        lastSimulationTime=sim.getSimulationTime()
    end
end
没有把线程自动切换关闭,也不使用sim.switchthread(),内循环会执行很多次,而中断只有一次。

4.2

function sysCall_threadmain()
    local lastSimulationTime=sim.getSimulationTime()
    local simulationStep=sim.getSimulationTimeStep()
    while true do
        local cnt=0
        -- Inner loop:
        while sim.getSimulationTime()==lastSimulationTime do
            cnt=cnt+1
            sim.switchThread()
        end
        printf('Inner loop executed %i times for one simulation step.',cnt)
        local dt=sim.getSimulationTime()-lastSimulationTime
        printf('Thread was interrupted %i times since last simulation step.',math.floor((dt/simulationStep)+0.5))
        lastSimulationTime=sim.getSimulationTime()
    end
end
线程自动切换没有关闭,使用了 sim.switchThread(),内循环只进行一次,中断也只进行一次。
4.3
function sysCall_threadmain()
    local lastSimulationTime=sim.getSimulationTime()
    local simulationStep=sim.getSimulationTimeStep()
    while true do
        local cnt=0
        -- Inner loop:
        while sim.getSimulationTime()==lastSimulationTime do
            for j=1,100000,1 do end -- delay loop
            cnt=cnt+1
            sim.switchThread()
        end
        printf('Inner loop executed %i times for one simulation step.',cnt)
        local dt=sim.getSimulationTime()-lastSimulationTime
        printf('Thread was interrupted %i times since last simulation step.',math.floor((dt/simulationStep)+0.5))
        lastSimulationTime=sim.getSimulationTime()
    end
end


没有关闭线程的自动切换,又增加了for 循环,内循环只进行了一次,但是由于线程会自动切换,中断发生了许多次。

4.4

function sysCall_threadmain()
    sim.setThreadAutomaticSwitch(false)
    local lastSimulationTime=sim.getSimulationTime()
    local simulationStep=sim.getSimulationTimeStep()
    while true do
        local cnt=0
        -- Inner loop:
        while sim.getSimulationTime()==lastSimulationTime do
            for j=1,100000,1 do end -- delay loop
            cnt=cnt+1
            sim.switchThread()
        end
        printf('Inner loop executed %i times for one simulation step.',cnt)
        local dt=sim.getSimulationTime()-lastSimulationTime
        printf('Thread was interrupted %i times since last simulation step.',math.floor((dt/simulationStep)+0.5))
        lastSimulationTime=sim.getSimulationTime()
    end
end
使用sim.setThreadAutomaticSwitch(false)关闭线程的自动切换,虽然同样是增加了for 循环,但是打印结果显示,内循环只会发生一次,中断也只发生一次(由于 sim.switchThread()而发生的一次).

参考:

进程、线程、多线程相关总结 

Clarification about "Thread related functionality"  

V-REP Forum - Search

how to simulate precise interrupt timing in V-Rep?  

Lua学习笔记-9.4章-非抢占式的多线程 - 


  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值