Going Parallel
| ← Previous (Hello World in ProcessJ) | Going Parallel | Next (Communicating Processes) → |
ProcessJ is a highly concurrent or parallel language. it is easy to write code that runs a number of procedures concurrently. A procedure running is called a process. To start two or more processes concurrently we use the keyword par (short for parallel). Here is a simple example that runs two processes -- hello() prints "Hello" and world() prints "World":
import std.io;
public proc void hello() {
println("Hello");
}
public proc void world() {
println("World");
}
public proc void main(string args[]) {
par {
hello();
world();
}
}
When this program is run, the hello() and the world() procedures are executed concurrently as processes; that is, they run at the same time. This means that there is no guarantee which word, "Hello" or "World", will be printed first. Since both processes run concurrently, either one could print first, so there are two different outputs from this program that are correct:
HelloWorld.pj
Hello
World
and
World
Hello
You may see the first one most of the time when running the program, but the second one is also possible: If the scheduler on a single-core CPU schedules world() before hello(), the second output will be produced. If we slightly alter the main() procedure to look like this:
import std.io;
public proc void hello() {
println("Hello");
}
public proc void world() {
println("World");
}
public proc void main(string args[]) {
par {
hello();
world();
}
println("Goodbye World");
}
the output of course changes to include the "Goodbye World" string. However, this string will always appear last. That is, we will see one of two possible outputs:
HelloWorld.pj
Hello
World
Goodbye World
and
World
Hello
Goodbye World
This is because the par block (par { ... }) will suspend the main() process until all processes in the par block have finished running. If we instead wrote this code (Note, we have moved the print statement into the par block):
(HelloWorldPar3.pj)
import std.io;
public proc void hello() {
println("Hello");
}
public proc void world() {
println("World");
}
public proc void main(string args[]) {
par {
hello();
world();
println("Goodbye World");
}
}
We would now be running three concurrent processes: hello(), world(), and the print("Goodbye World") statement -- all three concurrently. Now, how many possible output scenarios do we get now? Since all possible interleavings of the output are possible we get a total of 6 different output, including the ones that start with the string "Goodbye World".
It is worth noting something about a par block now: The opposite of a par block (a parallel block) is a seq block (a sequential block). We know sequential blocks from many programming languages. In Java, all blocks are sequential (if we ignore threading for a second). A sequential block is simply a number of statements separated by a semicolon inside a set of { }. This also holds true in ProcessJ, however, a sequential block may be prefixed with the keyword seq. So we could have written HelloWorldPar.pj in the following way:
(HelloWorldPar4.pj)
import std.io;
public proc void hello() {
println("Hello");
}
public proc void world() {
println("World");
}
public proc void main(string args[]) {
seq {
par {
hello();
world();
}
println("Goodbye World");
}
}
This style of programming is an inherited option from the language's grandfather, a another CSP-based language from the 80ies called occam. Note, The program would be equally valid if we omitted the seq keyword, but then we may as well also remove the corresponding set of { }.
| ← Previous (Hello World in ProcessJ) | Going Parallel | Next (Communicating Processes) → |