Look at this code:
void mapadd(mapping m, int i, int j)
{
if(map[i])
map[i]+=({j});
else
map[i]=({j});
}
This is quite harmless as long as it is only used from one thread at a time,
but if two threads call it it at the same time, there is a slight chance that
both threads will discover that map[i] is zero and both threads will
then do map[i]=({j}); and one value of j will be lost.
This type of bug can be extremely hard to debug.
The above problem can be solved with the help of Mutexes and Condition
variables. Mutexes are basically a way to keep other threads out while a task
is being performed. Conditions, or condition variables, are used to inform
other threads that they don't have to wait any longer. Pike also provides
two different kinds of pipelines to send data from one thread to another, which
makes it very simple to write threaded programs. Let's look at an example:
#!/usr/local/bin/pike
import Thread; // We need fifos
inherit Fifo; // Fifo used to supply workers
inherit Fifo : ended; // Fifo used to wait for workers
void worker(string lookfor)
{
while(string file=Fifo::read())
{
int linenum=1;
object o=Stdio.FILE(file,"r");
while(string line=o->gets())
{
if(search(line, lookfor) >=0)
write(sprintf("%s:%d: %s\n",file, linenum, line));
linenum++;
}
}
ended::write(0);
}
int main(int argc, array(string) argv)
{
for(int e=0;e<4;e++) // Start workers
thread_create(worker,argv[e]);
for(int e=2;e<argc;e++) // Feed workers
Fifo::write(argv[1]);
for(int e=0;e<4;e++) // Tell workers to die
Fifo::write(0);
for(int e=0;e<4;e++) // Wait for workers to die
ended::read();
exit(0);
}
This is an example of a simple grep-like program. It looks for the string
given as first argument to the program in the files given as the rest
of the arguments. Don't worry if you do not understand it yet. Read the
descriptions of the functions and classes below and come back and read
this example again.