• Aucun résultat trouvé

Single-Tasking vs. Multitasking

Dans le document Creating Cool MINDSTORMS® (Page 158-162)

Before introducing the programs for the Omni-Biped, let’s discuss single-task and multitask programs. In the simplest case, a program can be seen as a sequence of actions; for example:

• Go forward for three seconds.

• Turn left for two seconds.

• Wait for an incoming object.

• Go backward until the object goes out of sight.

• And so on . . .

The preceding sequence can be translated into an NXC program that has only one task—

the maintask, to be more precise. In fact, as you know, the first lines of code to be executed when you start a program are the ones inside the maintask. So, single-task programs can have only this task, and the flow of execution is specified by the code inside the maintask.

C H A P T E R 5■ O M N I - B I P E D 145

What do you do if you’d like to have more action sequences running together? You would have to use multitasking; that is, writing a program with more than one task, each one containing its own action sequence. For example, you should have a task to read a sensor continuously, another task to manage an animation on the NXT display, one to move the robot around, and one dealing with Bluetooth communication. The maintask is always present, of course, and you would have to tell the NXT firmware that you want to run more tasks together. You can do this in two ways. You can add the following statement at the top of main, so that the NXT will exe-cute the tasks listed inside the parentheses:

Precedes (task1, task2, ... , taskN);

These tasks will be started only when the task that called the Precedesstatement has ended its execution. Note that any task, not just main, can call Precedes.

The other way to tell the NXT to run multiple tasks is to add the following statement in every task that must be executed when the tasks listed in parentheses have ended:

Follows(main, task1, task2, ...);

To start multiple tasks at the beginning of the program, the maintask must contain only some initialization code (to initialize sensors or variables) and the Precedesstatement. So, the maintask exits at once, and the program flow is split up into the other tasks listed inside the Precedesparentheses. The alternative solution is to have an empty maintask, and to put aFollows(main)statement in every task you want to run.

In reality, the NXT processor executes one task at a time, switching from one to another so quickly that you have the illusion that all the tasks are running together. To be more precise, the part of the NXT firmware termed scheduleris responsible for this task execution switching.

Multitasking is also called concurrent programming, if the tasks in execution are sharing some resources, such as a sensor, an output device, a motor, or a display. This resource sharing must be well disciplined to avoid weird and unpredictable behavior in the program; this is the main trap of multitasking.

As an example to make this clearer, imagine a narrow kitchen of a restaurant, equipped with pots, ladles, but only one oven. If only one cook is at work, there’s no problem: he can use all the equipment with ease. However, if the restaurant owner decides to improve his kitchen, engaging one more cook and a chef, then things change a lot. In fact, the two cooks must now share the kitchen resources, while moving in such little space, and in particular, dealing with the only oven available. So, the chef would have to schedule the two cooks in the kitchen to avoid their becoming stuck, and would provide them a way to alternate using the oven, even-tually making one cook wait if the other one is using it.

Leaving the cooks to their work and coming back to NXC, you must find a way to avoid the scenario that more than one task accesses the same resource at the same time. I said that in reality the processor is executing only a task at a time. However, if the scheduler suspends the running task that is using the common resource, before the task has released the resource, then no other task (awakened by the scheduler) should access that resource.

For example, imagine task 1 playing a high-pitched tone at a certain rate; the sound has not been completely played when the scheduler halts task 1, putting task 2 into execution.

Task 2 plays a sound at a different frequency. If the sound output device usage is not properly managed, you would hear the sounds coming out of the NXT without order: the sound played by a task would interrupt the other task sound without waiting for it to finish—without discipline.

C H A P T E R 5■ O M N I - B I P E D 146

The tools that solve the preceding problem are special variables called semaphores, or mutexes(singular mutex, from mutual exclusion). You must use the shared resources only inside so-called critical sections—parts of code that must be included between an Acquire(mutex_name) statement and aRelease(mutex_name)statement:

Acquire (mtx);

/* critical section, where shared resources are used */

Release (mtx);

Acquiresimply waits for the mutex_namemutex variable to be released, and this waiting avoids conflicts with other tasks that require the shared resource. Release, as the term sug-gests, releases the mutex, thus allowing other tasks blocked at the Acquirestatement line to enter the critical section.

Note

The &(ampersand) before the argument name means that it is passed by reference, and thus can be modified by the function code. In NXC, you usually pass arguments by value: the variable xpassed by value to myfun(x)is not modified after the myfuncall. A temporary copy of the xvariable is made instead.

For details, check the NXC Programming Guide.

Let’s see this technique in practice, with the help of two simple multitasking programs.

The programs should do the same thing. They have two tasks running at the same time, one playing a high tone, the other playing a lower tone, alternately: high, low, high, low, and so on. Listing 5-1 shows the first, wrong version of the program.

Listing 5-1. The Wrong Version of a Simple Multitask Program to Play Sounds task task1()

{

while(true) {

C H A P T E R 5■ O M N I - B I P E D 147

PlayTone(1000,300);

Here you can see that the maintask is used just to tell the NXT scheduler to execute the tasks named task1and task2, after the maintask itself has finished. Because the Precedes statement is the only thing the maintask has to do, it ends at once, leaving space for the other two concurrent tasks. They are concurrent, in fact, because both are trying to play a tone, access-ing the NXT loudspeaker as a shared resource. The program does not behave as expected, because the tasks alternate with no discipline about the loudspeaker usage. The sound pattern coming out is random: it could be low, high, high, high, low, low, high, high, low, low, low, low, high . . . , instead of the regular pattern described before, where the high and low tones alternate in an orderly fashion. Now you should realize why you must employ mutex variables. See the correct version of the preceding program in Listing 5-2.

Listing 5-2. The Correct Version of a Simple Multitask Program to Play Alternating Sounds

mutex sound;

{

The program is the same, but now the critical sections—those parts of the task code where the shared resource is used—are enclosed by the Acquire/Releasestatements. The mutex variable is called sound, according to the function of the critical section it is meant to discipline. It is declared as a global variable, so that every function or task in the program can see and use it. The sound pattern coming out is the one you desired: high, low, high, low, high, low . . . The first sound to be played is always the high-pitched one, because task1comes before task2in the Precedesstatement list. The first scheduled task is task1, playing the high tone first. If you swap the task names inside parentheses, such as Precedes(task2,task1), the first sound heard will be the low-pitched one, played by task2.

Now you should have the essential background about multitasking to continue on.

Dans le document Creating Cool MINDSTORMS® (Page 158-162)