金宏和實です。
マルチタスクの説明するために、ポートBにカラーセンサー、ポートAにフォースセンサー(タッチセンサー)を接続した。超音波センサーもFポートに接続しているが今回は利用していない。
前回、Spikeアプリではrunloopモジュールを使ってマルチタスクが実現できるという話を書いたが、runloopモジュールを使えばマルチタスクになるというわけではない。まず、次のマルチタスクでないプログラムを見てもらいたい。

from hub import sound,port
import force_sensor
import runloop
import color
import color_sensor

def red_detected():
    return color_sensor.color(port.B) == color.RED

def force_pressed():
    return force_sensor.pressed(port.A)

async def main():
    while True:
        if red_detected():
            sound.beep(440, 1000000, 100)
            while red_detected():
                await runloop.sleep_ms(1)
        elif force_pressed():
            sound.beep(880, 200, 100)
            while force_pressed():
                await runloop.sleep_ms(1)
        else:
            sound.stop()

runloop.run(main())

上記のプログラムではasyncでmain関数を定義して、runloop.run(main())でmain関数を実行している。awaitで待つ処理も入れているのでマルチタスク処理を作っているように見える。しかし、main関数のwhileループの中のコードに注目してほしい。ポートBのカラーセンサーがif red_detected():でTrue(真)を返す間は、ポートAのフォースセンサーが押されているかどうかを検知することはない。つまり、赤信号で止まり、何かにぶつかったらバックしなさいとプログラムしても赤信号で止まっている間にフォースセンサーが押されてもロボットはバックしない。この問題を解決するには、赤信号で止まりなさいというタスクと何かにぶつかったらバックしなさいというタスクを同時に(厳密に言うと同時ではないが野球で言う同時アウトのような同時)動かさないといけない。それがマルチタスクだ。

runloopでコルーチンを複数runするとマルチタスクが実現できる。コルーチン(co-routine)とは処理を中断して、任意のタイミングで再開できるルーチンのことであるが、この説明だとちょっとわかりにくいだろう。
具体的に言うと、コルーチンにはawaitコマンドが一つ必要である。実行時間が短いwhile ループを使用する場合は、ループ内に await runloop.sleep_ms() を入れて、他のコルーチンが開始できるようする。つまり、他のコルーチンに実行できる隙を作るわけだ。そして、複数のコルーチンをrunloop.run()で実行する。

from hub import port, sound
import color
import color_sensor
import force_sensor
import runloop

def red_detected():
    return color_sensor.color(port.B) == color.RED

def force_pressed():
    return force_sensor.pressed(port.A)

async def check_color():
    while True:
        while not red_detected():
            await runloop.sleep_ms(1)
        sound.beep(440, 1000000, 100)
        while red_detected():
            await runloop.sleep_ms(1)
        sound.stop()

async def check_force_pressed():
    while True:        
        while not force_pressed():
            await runloop.sleep_ms(1)
        sound.beep(880, 200, 100)
        # Wait until the force sensor is released.
        while force_pressed():
            await runloop.sleep_ms(1)

runloop.run(check_color(), check_force_pressed())

上記のプログラムではcheck_color(), check_force_pressed()の二つのコルーチンを定義し、runloop.run()で実行している。カラーセンサーが赤色を見つけた場合もフォースセンサーが押された場合もsound.beep()でビープ音を鳴らすだけだが440、880と周波数を変えて区別できるようにしている。

このプログラムを実行し、カラーセンサーが赤色を見つけている間にフォースセンサーを押すと違う音が鳴る。