To contents Next section

10.1 Starting a thread

Starting a thread is very easy. You simply call thread_create with a function pointer and any arguments it needs and that function will be executed in a separate thread. The function thread_create will return immediately and both the calling function and the called function will execute at the same time. Example:
void foo(int x)
{
    for(int e=0;e<5;e++)
    {
        sleep(1);
        write("Hello from thread "+x+".\n");
    }
}

int main()
{
    thread_create(foo, 2);
    thread_create(foo, 3);
    foo(1);
}
This may all seem very simple, but there are a few complications to watch out for:
Accessing unlocked data
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.
Deadlocks
Deadlocks arise when two threads are waiting for each other to do something. This bug can often arise when several threads need access to a number of resources such as files or other I/O devices. What may happen is that one thread has locked device #1 and is trying to lock device #2 while another thread has locked device #2 and is trying to lock device #1. This type of bug is generally easier to find, but may require a lot of work to fix.

To contents Next section