Cari di Perl 
    Perl Tutorial
Daftar Isi
(Sebelumnya) Tutorial on pack and unpackTutorial for writing XSUBs (Berikutnya)
Tutorials

Tutorial on threads in Perl

Daftar Isi

NAME

perlthrtut - Tutorial on threads in Perl

DESCRIPTION

This tutorial describes the use of Perl interpreter threads (sometimesreferred to as ithreads) that was first introduced in Perl 5.6.0. In thismodel, each thread runs in its own Perl interpreter, and any data sharingbetween threads must be explicit. The user-level interface for ithreadsuses the threads class.

NOTE: There was another older Perl threading flavor called the 5.005 modelthat used the threads class. This old model was known to have problems, isdeprecated, and was removed for release 5.10. You arestrongly encouraged to migrate any existing 5.005 threads code to the newmodel as soon as possible.

You can see which (or neither) threading flavour you have byrunning perl -V and looking at the Platform section.If you have useithreads=define you have ithreads, if youhave use5005threads=define you have 5.005 threads.If you have neither, you don't have any thread support built in.If you have both, you are in trouble.

The threads and threads::shared modules are included in the core Perldistribution. Additionally, they are maintained as a separate modules onCPAN, so you can check there for any updates.

What Is A Thread Anyway?

A thread is a flow of control through a program with a singleexecution point.

Sounds an awful lot like a process, doesn't it? Well, it should.Threads are one of the pieces of a process. Every process has at leastone thread and, up until now, every process running Perl had only onethread. With 5.8, though, you can create extra threads. We're goingto show you how, when, and why.

Threaded Program Models

There are three basic ways that you can structure a threadedprogram. Which model you choose depends on what you need your programto do. For many non-trivial threaded programs, you'll need to choosedifferent models for different pieces of your program.

Boss/Worker

The boss/worker model usually has one boss thread and one or moreworker threads. The boss thread gathers or generates tasks that needto be done, then parcels those tasks out to the appropriate workerthread.

This model is common in GUI and server programs, where a main threadwaits for some event and then passes that event to the appropriateworker threads for processing. Once the event has been passed on, theboss thread goes back to waiting for another event.

The boss thread does relatively little work. While tasks aren'tnecessarily performed faster than with any other method, it tends tohave the best user-response times.

Work Crew

In the work crew model, several threads are created that doessentially the same thing to different pieces of data. It closelymirrors classical parallel processing and vector processors, where alarge array of processors do the exact same thing to many pieces ofdata.

This model is particularly useful if the system running the programwill distribute multiple threads across different processors. It canalso be useful in ray tracing or rendering engines, where theindividual threads can pass on interim results to give the user visualfeedback.

Pipeline

The pipeline model divides up a task into a series of steps, andpasses the results of one step on to the thread processing thenext. Each thread does one thing to each piece of data and passes theresults to the next thread in line.

This model makes the most sense if you have multiple processors so twoor more threads will be executing in parallel, though it can oftenmake sense in other contexts as well. It tends to keep the individualtasks small and simple, as well as allowing some parts of the pipelineto block (on I/O or system calls, for example) while other parts keepgoing. If you're running different parts of the pipeline on differentprocessors you may also take advantage of the caches on eachprocessor.

This model is also handy for a form of recursive programming where,rather than having a subroutine call itself, it instead createsanother thread. Prime and Fibonacci generators both map well to thisform of the pipeline model. (A version of a prime number generator ispresented later on.)

What kind of threads are Perl threads?

If you have experience with other thread implementations, you mightfind that things aren't quite what you expect. It's very important toremember when dealing with Perl threads that Perl Threads Are Not XThreads for all values of X. They aren't POSIX threads, orDecThreads, or Java's Green threads, or Win32 threads. There aresimilarities, and the broad concepts are the same, but if you startlooking for implementation details you're going to be eitherdisappointed or confused. Possibly both.

This is not to say that Perl threads are completely different fromeverything that's ever come before. They're not. Perl's threadingmodel owes a lot to other thread models, especially POSIX. Just asPerl is not C, though, Perl threads are not POSIX threads. So if youfind yourself looking for mutexes, or thread priorities, it's time tostep back a bit and think about what you want to do and how Perl cando it.

However, it is important to remember that Perl threads cannot magicallydo things unless your operating system's threads allow it. So if yoursystem blocks the entire process on sleep(), Perl usually will, as well.

Perl Threads Are Different.

Thread-Safe Modules

The addition of threads has changed Perl's internalssubstantially. There are implications for people who writemodules with XS code or external libraries. However, since Perl data isnot shared among threads by default, Perl modules stand a high chance ofbeing thread-safe or can be made thread-safe easily. Modules that are nottagged as thread-safe should be tested or code reviewed before being usedin production code.

Not all modules that you might use are thread-safe, and you shouldalways assume a module is unsafe unless the documentation saysotherwise. This includes modules that are distributed as part of thecore. Threads are a relatively new feature, and even some of the standardmodules aren't thread-safe.

Even if a module is thread-safe, it doesn't mean that the module is optimizedto work well with threads. A module could possibly be rewritten to utilizethe new features in threaded Perl to increase performance in a threadedenvironment.

If you're using a module that's not thread-safe for some reason, youcan protect yourself by using it from one, and only one thread at all.If you need multiple threads to access such a module, you can use semaphores andlots of programming discipline to control access to it. Semaphoresare covered in Basic semaphores.

See also Thread-Safety of System Libraries.

Thread Basics

The threads module provides the basic functions you need to writethreaded programs. In the following sections, we'll cover the basics,showing you what you need to do to create a threaded program. Afterthat, we'll go over some of the features of the threads module thatmake threaded programming easier.

Basic Thread Support

Thread support is a Perl compile-time option. It's something that'sturned on or off when Perl is built at your site, rather than whenyour programs are compiled. If your Perl wasn't compiled with threadsupport enabled, then any attempt to use threads will fail.

Your programs can use the Config module to check whether threads areenabled. If your program can't run without them, you can say somethinglike:

  1. use Config;
  2. $Config{useithreads} or die('Recompile Perl with threads to run this program.');

A possibly-threaded program using a possibly-threaded module mighthave code like this:

  1. use Config;
  2. use MyMod;
  3. BEGIN {
  4. if ($Config{useithreads}) {
  5. # We have threads
  6. require MyMod_threaded;
  7. import MyMod_threaded;
  8. } else {
  9. require MyMod_unthreaded;
  10. import MyMod_unthreaded;
  11. }
  12. }

Since code that runs both with and without threads is usually prettymessy, it's best to isolate the thread-specific code in its ownmodule. In our example above, that's what MyMod_threaded is, and it'sonly imported if we're running on a threaded Perl.

A Note about the Examples

In a real situation, care should be taken that all threads are finishedexecuting before the program exits. That care has not been taken in theseexamples in the interest of simplicity. Running these examples as is willproduce error messages, usually caused by the fact that there are stillthreads running when the program exits. You should not be alarmed by this.

Creating Threads

The threads module provides the tools you need to create newthreads. Like any other module, you need to tell Perl that you want to useit; use threads; imports all the pieces you need to create basicthreads.

The simplest, most straightforward way to create a thread is with create():

  1. use threads;
  2. my $thr = threads->create(\&sub1);
  3. sub sub1 {
  4. print("In the thread\n");
  5. }

The create() method takes a reference to a subroutine and creates a newthread that starts executing in the referenced subroutine. Controlthen passes both to the subroutine and the caller.

If you need to, your program can pass parameters to the subroutine aspart of the thread startup. Just include the list of parameters aspart of the threads->create() call, like this:

  1. use threads;
  2. my $Param3 = 'foo';
  3. my $thr1 = threads->create(\&sub1, 'Param 1', 'Param 2', $Param3);
  4. my @ParamList = (42, 'Hello', 3.14);
  5. my $thr2 = threads->create(\&sub1, @ParamList);
  6. my $thr3 = threads->create(\&sub1, qw(Param1 Param2 Param3));
  7. sub sub1 {
  8. my @InboundParameters = @_;
  9. print("In the thread\n");
  10. print('Got parameters >', join('<>', @InboundParameters), "<\n");
  11. }

The last example illustrates another feature of threads. You can spawnoff several threads using the same subroutine. Each thread executesthe same subroutine, but in a separate thread with a separateenvironment and potentially separate arguments.

new() is a synonym for create().

Waiting For A Thread To Exit

Since threads are also subroutines, they can return values. To waitfor a thread to exit and extract any values it might return, you canuse the join() method:

  1. use threads;
  2. my ($thr) = threads->create(\&sub1);
  3. my @ReturnData = $thr->join();
  4. print('Thread returned ', join(', ', @ReturnData), "\n");
  5. sub sub1 { return ('Fifty-six', 'foo', 2); }

In the example above, the join() method returns as soon as the threadends. In addition to waiting for a thread to finish and gathering upany values that the thread might have returned, join() also performsany OS cleanup necessary for the thread. That cleanup might beimportant, especially for long-running programs that spawn lots ofthreads. If you don't want the return values and don't want to waitfor the thread to finish, you should call the detach() methodinstead, as described next.

NOTE: In the example above, the thread returns a list, thus necessitatingthat the thread creation call be made in list context (i.e., my ($thr)).See $thr->join() in threads and THREAD CONTEXT in threads for moredetails on thread context and return values.

Ignoring A Thread

join() does three things: it waits for a thread to exit, cleans upafter it, and returns any data the thread may have produced. But whatif you're not interested in the thread's return values, and you don'treally care when the thread finishes? All you want is for the threadto get cleaned up after when it's done.

In this case, you use the detach() method. Once a thread is detached,it'll run until it's finished; then Perl will clean up after itautomatically.

  1. use threads;
  2. my $thr = threads->create(\&sub1); # Spawn the thread
  3. $thr->detach(); # Now we officially don't care any more
  4. sleep(15); # Let thread run for awhile
  5. sub sub1 {
  6. $a = 0;
  7. while (1) {
  8. $a++;
  9. print("\$a is $a\n");
  10. sleep(1);
  11. }
  12. }

Once a thread is detached, it may not be joined, and any return datathat it might have produced (if it was done and waiting for a join) islost.

detach() can also be called as a class method to allow a thread todetach itself:

  1. use threads;
  2. my $thr = threads->create(\&sub1);
  3. sub sub1 {
  4. threads->detach();
  5. # Do more work
  6. }

Process and Thread Termination

With threads one must be careful to make sure they all have a chance torun to completion, assuming that is what you want.

An action that terminates a process will terminate all runningthreads. die() and exit() have this property,and perl does an exit when the main thread exits,perhaps implicitly by falling off the end of your code,even if that's not what you want.

As an example of this case, this code prints the message"Perl exited with active threads: 2 running and unjoined":

  1. use threads;
  2. my $thr1 = threads->new(\&thrsub, "test1");
  3. my $thr2 = threads->new(\&thrsub, "test2");
  4. sub thrsub {
  5. my ($message) = @_;
  6. sleep 1;
  7. print "thread $message\n";
  8. }

But when the following lines are added at the end:

  1. $thr1->join();
  2. $thr2->join();

it prints two lines of output, a perhaps more useful outcome.

Threads And Data

Now that we've covered the basics of threads, it's time for our nexttopic: Data. Threading introduces a couple of complications to dataaccess that non-threaded programs never need to worry about.

Shared And Unshared Data

The biggest difference between Perl ithreads and the old 5.005 stylethreading, or for that matter, to most other threading systems out there,is that by default, no data is shared. When a new Perl thread is created,all the data associated with the current thread is copied to the newthread, and is subsequently private to that new thread!This is similar in feel to what happens when a Unix process forks,except that in this case, the data is just copied to a different part ofmemory within the same process rather than a real fork taking place.

To make use of threading, however, one usually wants the threads to shareat least some data between themselves. This is done with thethreads::shared module and the :shared attribute:

  1. use threads;
  2. use threads::shared;
  3. my $foo :shared = 1;
  4. my $bar = 1;
  5. threads->create(sub { $foo++; $bar++; })->join();
  6. print("$foo\n"); # Prints 2 since $foo is shared
  7. print("$bar\n"); # Prints 1 since $bar is not shared

In the case of a shared array, all the array's elements are shared, and fora shared hash, all the keys and values are shared. This placesrestrictions on what may be assigned to shared array and hash elements: onlysimple values or references to shared variables are allowed - this isso that a private variable can't accidentally become shared. A badassignment will cause the thread to die. For example:

  1. use threads;
  2. use threads::shared;
  3. my $var = 1;
  4. my $svar :shared = 2;
  5. my %hash :shared;
  6. ... create some threads ...
  7. $hash{a} = 1; # All threads see exists($hash{a}) and $hash{a} == 1
  8. $hash{a} = $var; # okay - copy-by-value: same effect as previous
  9. $hash{a} = $svar; # okay - copy-by-value: same effect as previous
  10. $hash{a} = \$svar; # okay - a reference to a shared variable
  11. $hash{a} = \$var; # This will die
  12. delete($hash{a}); # okay - all threads will see !exists($hash{a})

Note that a shared variable guarantees that if two or more threads try tomodify it at the same time, the internal state of the variable will notbecome corrupted. However, there are no guarantees beyond this, asexplained in the next section.

Thread Pitfalls: Races

While threads bring a new set of useful tools, they also bring anumber of pitfalls. One pitfall is the race condition:

  1. use threads;
  2. use threads::shared;
  3. my $a :shared = 1;
  4. my $thr1 = threads->create(\&sub1);
  5. my $thr2 = threads->create(\&sub2);
  6. $thr1->join();
  7. $thr2->join();
  8. print("$a\n");
  9. sub sub1 { my $foo = $a; $a = $foo + 1; }
  10. sub sub2 { my $bar = $a; $a = $bar + 1; }

What do you think $a will be? The answer, unfortunately, is itdepends. Both sub1() and sub2() access the global variable $a, onceto read and once to write. Depending on factors ranging from yourthread implementation's scheduling algorithm to the phase of the moon,$a can be 2 or 3.

Race conditions are caused by unsynchronized access to shareddata. Without explicit synchronization, there's no way to be sure thatnothing has happened to the shared data between the time you access itand the time you update it. Even this simple code fragment has thepossibility of error:

  1. use threads;
  2. my $a :shared = 2;
  3. my $b :shared;
  4. my $c :shared;
  5. my $thr1 = threads->create(sub { $b = $a; $a = $b + 1; });
  6. my $thr2 = threads->create(sub { $c = $a; $a = $c + 1; });
  7. $thr1->join();
  8. $thr2->join();

Two threads both access $a. Each thread can potentially be interruptedat any point, or be executed in any order. At the end, $a could be 3or 4, and both $b and $c could be 2 or 3.

Even $a += 5 or $a++ are not guaranteed to be atomic.

Whenever your program accesses data or resources that can be accessedby other threads, you must take steps to coordinate access or riskdata inconsistency and race conditions. Note that Perl will protect itsinternals from your race conditions, but it won't protect you from you.

Synchronization and control

Perl provides a number of mechanisms to coordinate the interactionsbetween themselves and their data, to avoid race conditions and the like.Some of these are designed to resemble the common techniques used in threadlibraries such as pthreads; others are Perl-specific. Often, thestandard techniques are clumsy and difficult to get right (such ascondition waits). Where possible, it is usually easier to use Perlishtechniques such as queues, which remove some of the hard work involved.

Controlling access: lock()

The lock() function takes a shared variable and puts a lock on it.No other thread may lock the variable until the variable is unlockedby the thread holding the lock. Unlocking happens automaticallywhen the locking thread exits the block that contains the call to thelock() function. Using lock() is straightforward: This example hasseveral threads doing some calculations in parallel, and occasionallyupdating a running total:

  1. use threads;
  2. use threads::shared;
  3. my $total :shared = 0;
  4. sub calc {
  5. while (1) {
  6. my $result;
  7. # (... do some calculations and set $result ...)
  8. {
  9. lock($total); # Block until we obtain the lock
  10. $total += $result;
  11. } # Lock implicitly released at end of scope
  12. last if $result == 0;
  13. }
  14. }
  15. my $thr1 = threads->create(\&calc);
  16. my $thr2 = threads->create(\&calc);
  17. my $thr3 = threads->create(\&calc);
  18. $thr1->join();
  19. $thr2->join();
  20. $thr3->join();
  21. print("total=$total\n");

lock() blocks the thread until the variable being locked isavailable. When lock() returns, your thread can be sure that no otherthread can lock that variable until the block containing thelock exits.

It's important to note that locks don't prevent access to the variablein question, only lock attempts. This is in keeping with Perl'slongstanding tradition of courteous programming, and the advisory filelocking that flock() gives you.

You may lock arrays and hashes as well as scalars. Locking an array,though, will not block subsequent locks on array elements, just lockattempts on the array itself.

Locks are recursive, which means it's okay for a thread tolock a variable more than once. The lock will last until the outermostlock() on the variable goes out of scope. For example:

  1. my $x :shared;
  2. doit();
  3. sub doit {
  4. {
  5. {
  6. lock($x); # Wait for lock
  7. lock($x); # NOOP - we already have the lock
  8. {
  9. lock($x); # NOOP
  10. {
  11. lock($x); # NOOP
  12. lockit_some_more();
  13. }
  14. }
  15. } # *** Implicit unlock here ***
  16. }
  17. }
  18. sub lockit_some_more {
  19. lock($x); # NOOP
  20. } # Nothing happens here

Note that there is no unlock() function - the only way to unlock avariable is to allow it to go out of scope.

A lock can either be used to guard the data contained within the variablebeing locked, or it can be used to guard something else, like a sectionof code. In this latter case, the variable in question does not hold anyuseful data, and exists only for the purpose of being locked. In thisrespect, the variable behaves like the mutexes and basic semaphores oftraditional thread libraries.

A Thread Pitfall: Deadlocks

Locks are a handy tool to synchronize access to data, and using themproperly is the key to safe shared data. Unfortunately, locks aren'twithout their dangers, especially when multiple locks are involved.Consider the following code:

  1. use threads;
  2. my $a :shared = 4;
  3. my $b :shared = 'foo';
  4. my $thr1 = threads->create(sub {
  5. lock($a);
  6. sleep(20);
  7. lock($b);
  8. });
  9. my $thr2 = threads->create(sub {
  10. lock($b);
  11. sleep(20);
  12. lock($a);
  13. });

This program will probably hang until you kill it. The only way itwon't hang is if one of the two threads acquires both locksfirst. A guaranteed-to-hang version is more complicated, but theprinciple is the same.

The first thread will grab a lock on $a, then, after a pause during whichthe second thread has probably had time to do some work, try to grab alock on $b. Meanwhile, the second thread grabs a lock on $b, then latertries to grab a lock on $a. The second lock attempt for both threads willblock, each waiting for the other to release its lock.

This condition is called a deadlock, and it occurs whenever two ormore threads are trying to get locks on resources that the othersown. Each thread will block, waiting for the other to release a lockon a resource. That never happens, though, since the thread with theresource is itself waiting for a lock to be released.

There are a number of ways to handle this sort of problem. The bestway is to always have all threads acquire locks in the exact sameorder. If, for example, you lock variables $a, $b, and $c, always lock$a before $b, and $b before $c. It's also best to hold on to locks foras short a period of time to minimize the risks of deadlock.

The other synchronization primitives described below can suffer fromsimilar problems.

Queues: Passing Data Around

A queue is a special thread-safe object that lets you put data in oneend and take it out the other without having to worry aboutsynchronization issues. They're pretty straightforward, and look likethis:

  1. use threads;
  2. use Thread::Queue;
  3. my $DataQueue = Thread::Queue->new();
  4. my $thr = threads->create(sub {
  5. while (my $DataElement = $DataQueue->dequeue()) {
  6. print("Popped $DataElement off the queue\n");
  7. }
  8. });
  9. $DataQueue->enqueue(12);
  10. $DataQueue->enqueue("A", "B", "C");
  11. sleep(10);
  12. $DataQueue->enqueue(undef);
  13. $thr->join();

You create the queue with Thread::Queue->new(). Then you canadd lists of scalars onto the end with enqueue(), and pop scalars offthe front of it with dequeue(). A queue has no fixed size, and can growas needed to hold everything pushed on to it.

If a queue is empty, dequeue() blocks until another thread enqueuessomething. This makes queues ideal for event loops and othercommunications between threads.

Semaphores: Synchronizing Data Access

Semaphores are a kind of generic locking mechanism. In their most basicform, they behave very much like lockable scalars, except that theycan't hold data, and that they must be explicitly unlocked. In theiradvanced form, they act like a kind of counter, and can allow multiplethreads to have the lock at any one time.

Basic semaphores

Semaphores have two methods, down() and up(): down() decrements the resourcecount, while up() increments it. Calls to down() will block if thesemaphore's current count would decrement below zero. This programgives a quick demonstration:

  1. use threads;
  2. use Thread::Semaphore;
  3. my $semaphore = Thread::Semaphore->new();
  4. my $GlobalVariable :shared = 0;
  5. $thr1 = threads->create(\&sample_sub, 1);
  6. $thr2 = threads->create(\&sample_sub, 2);
  7. $thr3 = threads->create(\&sample_sub, 3);
  8. sub sample_sub {
  9. my $SubNumber = shift(@_);
  10. my $TryCount = 10;
  11. my $LocalCopy;
  12. sleep(1);
  13. while ($TryCount--) {
  14. $semaphore->down();
  15. $LocalCopy = $GlobalVariable;
  16. print("$TryCount tries left for sub $SubNumber (\$GlobalVariable is $GlobalVariable)\n");
  17. sleep(2);
  18. $LocalCopy++;
  19. $GlobalVariable = $LocalCopy;
  20. $semaphore->up();
  21. }
  22. }
  23. $thr1->join();
  24. $thr2->join();
  25. $thr3->join();

The three invocations of the subroutine all operate in sync. Thesemaphore, though, makes sure that only one thread is accessing theglobal variable at once.

Advanced Semaphores

By default, semaphores behave like locks, letting only one threaddown() them at a time. However, there are other uses for semaphores.

Each semaphore has a counter attached to it. By default, semaphores arecreated with the counter set to one, down() decrements the counter byone, and up() increments by one. However, we can override any or allof these defaults simply by passing in different values:

  1. use threads;
  2. use Thread::Semaphore;
  3. my $semaphore = Thread::Semaphore->new(5);
  4. # Creates a semaphore with the counter set to five
  5. my $thr1 = threads->create(\&sub1);
  6. my $thr2 = threads->create(\&sub1);
  7. sub sub1 {
  8. $semaphore->down(5); # Decrements the counter by five
  9. # Do stuff here
  10. $semaphore->up(5); # Increment the counter by five
  11. }
  12. $thr1->detach();
  13. $thr2->detach();

If down() attempts to decrement the counter below zero, it blocks untilthe counter is large enough. Note that while a semaphore can be createdwith a starting count of zero, any up() or down() always changes thecounter by at least one, and so $semaphore->down(0) is the same as$semaphore->down(1).

The question, of course, is why would you do something like this? Whycreate a semaphore with a starting count that's not one, or whydecrement or increment it by more than one? The answer is resourceavailability. Many resources that you want to manage access for can besafely used by more than one thread at once.

For example, let's take a GUI driven program. It has a semaphore thatit uses to synchronize access to the display, so only one thread isever drawing at once. Handy, but of course you don't want any threadto start drawing until things are properly set up. In this case, youcan create a semaphore with a counter set to zero, and up it whenthings are ready for drawing.

Semaphores with counters greater than one are also useful forestablishing quotas. Say, for example, that you have a number ofthreads that can do I/O at once. You don't want all the threadsreading or writing at once though, since that can potentially swampyour I/O channels, or deplete your process's quota of filehandles. Youcan use a semaphore initialized to the number of concurrent I/Orequests (or open files) that you want at any one time, and have yourthreads quietly block and unblock themselves.

Larger increments or decrements are handy in those cases where athread needs to check out or return a number of resources at once.

Waiting for a Condition

The functions cond_wait() and cond_signal()can be used in conjunction with locks to notifyco-operating threads that a resource has become available. They arevery similar in use to the functions found in pthreads. Howeverfor most purposes, queues are simpler to use and more intuitive. Seethreads::shared for more details.

Giving up control

There are times when you may find it useful to have a threadexplicitly give up the CPU to another thread. You may be doing somethingprocessor-intensive and want to make sure that the user-interface threadgets called frequently. Regardless, there are times that you might wanta thread to give up the processor.

Perl's threading package provides the yield() function that doesthis. yield() is pretty straightforward, and works like this:

  1. use threads;
  2. sub loop {
  3. my $thread = shift;
  4. my $foo = 50;
  5. while($foo--) { print("In thread $thread\n"); }
  6. threads->yield();
  7. $foo = 50;
  8. while($foo--) { print("In thread $thread\n"); }
  9. }
  10. my $thr1 = threads->create(\&loop, 'first');
  11. my $thr2 = threads->create(\&loop, 'second');
  12. my $thr3 = threads->create(\&loop, 'third');

It is important to remember that yield() is only a hint to give up the CPU,it depends on your hardware, OS and threading libraries what actually happens.On many operating systems, yield() is a no-op. Therefore it is importantto note that one should not build the scheduling of the threads aroundyield() calls. It might work on your platform but it won't work on anotherplatform.

General Thread Utility Routines

We've covered the workhorse parts of Perl's threading package, andwith these tools you should be well on your way to writing threadedcode and packages. There are a few useful little pieces that didn'treally fit in anyplace else.

What Thread Am I In?

The threads->self() class method provides your program with a way toget an object representing the thread it's currently in. You can use thisobject in the same way as the ones returned from thread creation.

Thread IDs

tid() is a thread object method that returns the thread ID of thethread the object represents. Thread IDs are integers, with the mainthread in a program being 0. Currently Perl assigns a unique TID toevery thread ever created in your program, assigning the first threadto be created a TID of 1, and increasing the TID by 1 for each newthread that's created. When used as a class method, threads->tid()can be used by a thread to get its own TID.

Are These Threads The Same?

The equal() method takes two thread objects and returns trueif the objects represent the same thread, and false if they don't.

Thread objects also have an overloaded == comparison so that you can docomparison on them as you would with normal objects.

What Threads Are Running?

threads->list() returns a list of thread objects, one for each threadthat's currently running and not detached. Handy for a number of things,including cleaning up at the end of your program (from the main Perl thread,of course):

  1. # Loop through all the threads
  2. foreach my $thr (threads->list()) {
  3. $thr->join();
  4. }

If some threads have not finished running when the main Perl threadends, Perl will warn you about it and die, since it is impossible for Perlto clean up itself while other threads are running.

NOTE: The main Perl thread (thread 0) is in a detached state, and sodoes not appear in the list returned by threads->list().

A Complete Example

Confused yet? It's time for an example program to show some of thethings we've covered. This program finds prime numbers using threads.

  1. 1 #!/usr/bin/perl
  2. 2 # prime-pthread, courtesy of Tom Christiansen
  3. 3
  4. 4 use strict;
  5. 5 use warnings;
  6. 6
  7. 7 use threads;
  8. 8 use Thread::Queue;
  9. 9
  10. 10 sub check_num {
  11. 11 my ($upstream, $cur_prime) = @_;
  12. 12 my $kid;
  13. 13 my $downstream = Thread::Queue->new();
  14. 14 while (my $num = $upstream->dequeue()) {
  15. 15 next unless ($num % $cur_prime);
  16. 16 if ($kid) {
  17. 17 $downstream->enqueue($num);
  18. 18 } else {
  19. 19 print("Found prime: $num\n");
  20. 20 $kid = threads->create(\&check_num, $downstream, $num);
  21. 21 if (! $kid) {
  22. 22 warn("Sorry. Ran out of threads.\n");
  23. 23 last;
  24. 24 }
  25. 25 }
  26. 26 }
  27. 27 if ($kid) {
  28. 28 $downstream->enqueue(undef);
  29. 29 $kid->join();
  30. 30 }
  31. 31 }
  32. 32
  33. 33 my $stream = Thread::Queue->new(3..1000, undef);
  34. 34 check_num($stream, 2);

This program uses the pipeline model to generate prime numbers. Eachthread in the pipeline has an input queue that feeds numbers to bechecked, a prime number that it's responsible for, and an output queueinto which it funnels numbers that have failed the check. If the threadhas a number that's failed its check and there's no child thread, thenthe thread must have found a new prime number. In that case, a newchild thread is created for that prime and stuck on the end of thepipeline.

This probably sounds a bit more confusing than it really is, so let'sgo through this program piece by piece and see what it does. (Forthose of you who might be trying to remember exactly what a primenumber is, it's a number that's only evenly divisible by itself and 1.)

The bulk of the work is done by the check_num() subroutine, whichtakes a reference to its input queue and a prime number that it'sresponsible for. After pulling in the input queue and the prime thatthe subroutine is checking (line 11), we create a new queue (line 13)and reserve a scalar for the thread that we're likely to create later(line 12).

The while loop from line 14 to line 26 grabs a scalar off the inputqueue and checks against the prime this thread is responsiblefor. Line 15 checks to see if there's a remainder when we divide thenumber to be checked by our prime. If there is one, the numbermust not be evenly divisible by our prime, so we need to either passit on to the next thread if we've created one (line 17) or create anew thread if we haven't.

The new thread creation is line 20. We pass on to it a reference tothe queue we've created, and the prime number we've found. In lines 21through 24, we check to make sure that our new thread got created, andif not, we stop checking any remaining numbers in the queue.

Finally, once the loop terminates (because we got a 0 or undef in thequeue, which serves as a note to terminate), we pass on the notice to ourchild, and wait for it to exit if we've created a child (lines 27 and30).

Meanwhile, back in the main thread, we first create a queue (line 33) andqueue up all the numbers from 3 to 1000 for checking, plus a terminationnotice. Then all we have to do to get the ball rolling is pass the queueand the first prime to the check_num() subroutine (line 34).

That's how it works. It's pretty simple; as with many Perl programs,the explanation is much longer than the program.

Different implementations of threads

Some background on thread implementations from the operating systemviewpoint. There are three basic categories of threads: user-mode threads,kernel threads, and multiprocessor kernel threads.

User-mode threads are threads that live entirely within a program andits libraries. In this model, the OS knows nothing about threads. Asfar as it's concerned, your process is just a process.

This is the easiest way to implement threads, and the way most OSesstart. The big disadvantage is that, since the OS knows nothing aboutthreads, if one thread blocks they all do. Typical blocking activitiesinclude most system calls, most I/O, and things like sleep().

Kernel threads are the next step in thread evolution. The OS knowsabout kernel threads, and makes allowances for them. The maindifference between a kernel thread and a user-mode thread isblocking. With kernel threads, things that block a single thread don'tblock other threads. This is not the case with user-mode threads,where the kernel blocks at the process level and not the thread level.

This is a big step forward, and can give a threaded program quite aperformance boost over non-threaded programs. Threads that blockperforming I/O, for example, won't block threads that are doing otherthings. Each process still has only one thread running at once,though, regardless of how many CPUs a system might have.

Since kernel threading can interrupt a thread at any time, they willuncover some of the implicit locking assumptions you may make in yourprogram. For example, something as simple as $a = $a + 2 can behaveunpredictably with kernel threads if $a is visible to otherthreads, as another thread may have changed $a between the time itwas fetched on the right hand side and the time the new value isstored.

Multiprocessor kernel threads are the final step in threadsupport. With multiprocessor kernel threads on a machine with multipleCPUs, the OS may schedule two or more threads to run simultaneously ondifferent CPUs.

This can give a serious performance boost to your threaded program,since more than one thread will be executing at the same time. As atradeoff, though, any of those nagging synchronization issues thatmight not have shown with basic kernel threads will appear with avengeance.

In addition to the different levels of OS involvement in threads,different OSes (and different thread implementations for a particularOS) allocate CPU cycles to threads in different ways.

Cooperative multitasking systems have running threads give up controlif one of two things happen. If a thread calls a yield function, itgives up control. It also gives up control if the thread doessomething that would cause it to block, such as perform I/O. In acooperative multitasking implementation, one thread can starve all theothers for CPU time if it so chooses.

Preemptive multitasking systems interrupt threads at regular intervalswhile the system decides which thread should run next. In a preemptivemultitasking system, one thread usually won't monopolize the CPU.

On some systems, there can be cooperative and preemptive threadsrunning simultaneously. (Threads running with realtime prioritiesoften behave cooperatively, for example, while threads running atnormal priorities behave preemptively.)

Most modern operating systems support preemptive multitasking nowadays.

Performance considerations

The main thing to bear in mind when comparing Perl's ithreads to other threadingmodels is the fact that for each new thread created, a complete copy ofall the variables and data of the parent thread has to be taken. Thus,thread creation can be quite expensive, both in terms of memory usage andtime spent in creation. The ideal way to reduce these costs is to have arelatively short number of long-lived threads, all created fairly earlyon (before the base thread has accumulated too much data). Of course, thismay not always be possible, so compromises have to be made. However, aftera thread has been created, its performance and extra memory usage shouldbe little different than ordinary code.

Also note that under the current implementation, shared variablesuse a little more memory and are a little slower than ordinary variables.

Process-scope Changes

Note that while threads themselves are separate execution threads andPerl data is thread-private unless explicitly shared, the threads canaffect process-scope state, affecting all the threads.

The most common example of this is changing the current workingdirectory using chdir(). One thread calls chdir(), and the workingdirectory of all the threads changes.

Even more drastic example of a process-scope change is chroot():the root directory of all the threads changes, and no thread canundo it (as opposed to chdir()).

Further examples of process-scope changes include umask() andchanging uids and gids.

Thinking of mixing fork() and threads? Please lie down and waituntil the feeling passes. Be aware that the semantics of fork() varybetween platforms. For example, some Unix systems copy all the currentthreads into the child process, while others only copy the thread thatcalled fork(). You have been warned!

Similarly, mixing signals and threads may be problematic.Implementations are platform-dependent, and even the POSIXsemantics may not be what you expect (and Perl doesn't evengive you the full POSIX API). For example, there is no way toguarantee that a signal sent to a multi-threaded Perl applicationwill get intercepted by any particular thread. (However, a recentlyadded feature does provide the capability to send signals betweenthreads. See THREAD SIGNALLING in threads for more details.)

Thread-Safety of System Libraries

Whether various library calls are thread-safe is outside the controlof Perl. Calls often suffering from not being thread-safe include:localtime(), gmtime(), functions fetching user, group andnetwork information (such as getgrent(), gethostent(),getnetent() and so on), readdir(), rand(), and srand(). Ingeneral, calls that depend on some global external state.

If the system Perl is compiled in has thread-safe variants of suchcalls, they will be used. Beyond that, Perl is at the mercy ofthe thread-safety or -unsafety of the calls. Please consult yourC library call documentation.

On some platforms the thread-safe library interfaces may fail if theresult buffer is too small (for example the user group databases maybe rather large, and the reentrant interfaces may have to carry arounda full snapshot of those databases). Perl will start with a smallbuffer, but keep retrying and growing the result bufferuntil the result fits. If this limitless growing sounds bad forsecurity or memory consumption reasons you can recompile Perl withPERL_REENTRANT_MAXSIZE defined to the maximum number of bytes you willallow.

Conclusion

A complete thread tutorial could fill a book (and has, many times),but with what we've covered in this introduction, you should be wellon your way to becoming a threaded Perl expert.

SEE ALSO

Annotated POD for threads:http://annocpan.org/?mode=search&field=Module&name=threads

Latest version of threads on CPAN:http://search.cpan.org/search?module=threads

Annotated POD for threads::shared:http://annocpan.org/?mode=search&field=Module&name=threads%3A%3Ashared

Latest version of threads::shared on CPAN:http://search.cpan.org/search?module=threads%3A%3Ashared

Perl threads mailing list:http://lists.perl.org/list/ithreads.html

Bibliography

Here's a short bibliography courtesy of Jürgen Christoffel:

Introductory Texts

Birrell, Andrew D. An Introduction to Programming withThreads. Digital Equipment Corporation, 1989, DEC-SRC Research Report#35 online asftp://ftp.dec.com/pub/DEC/SRC/research-reports/SRC-035.pdf(highly recommended)

Robbins, Kay. A., and Steven Robbins. Practical Unix Programming: AGuide to Concurrency, Communication, andMultithreading. Prentice-Hall, 1996.

Lewis, Bill, and Daniel J. Berg. Multithreaded Programming withPthreads. Prentice Hall, 1997, ISBN 0-13-443698-9 (a well-writtenintroduction to threads).

Nelson, Greg (editor). Systems Programming with Modula-3. PrenticeHall, 1991, ISBN 0-13-590464-1.

Nichols, Bradford, Dick Buttlar, and Jacqueline Proulx Farrell.Pthreads Programming. O'Reilly & Associates, 1996, ISBN 156592-115-1(covers POSIX threads).

OS-Related References

Boykin, Joseph, David Kirschen, Alan Langerman, and SusanLoVerso. Programming under Mach. Addison-Wesley, 1994, ISBN0-201-52739-1.

Tanenbaum, Andrew S. Distributed Operating Systems. Prentice Hall,1995, ISBN 0-13-219908-4 (great textbook).

Silberschatz, Abraham, and Peter B. Galvin. Operating System Concepts,4th ed. Addison-Wesley, 1995, ISBN 0-201-59292-4

Other References

Arnold, Ken and James Gosling. The Java Programming Language, 2nded. Addison-Wesley, 1998, ISBN 0-201-31006-6.

comp.programming.threads FAQ,http://www.serpentine.com/~bos/threads-faq/

Le Sergent, T. and B. Berthomieu. "Incremental MultiThreaded GarbageCollection on Virtually Shared Memory Architectures" in MemoryManagement: Proc. of the International Workshop IWMM 92, St. Malo,France, September 1992, Yves Bekkers and Jacques Cohen, eds. Springer,1992, ISBN 3540-55940-X (real-life thread applications).

Artur Bergman, "Where Wizards Fear To Tread", June 11, 2002,http://www.perl.com/pub/a/2002/06/11/threads.html

Acknowledgements

Thanks (in no particular order) to Chaim Frenkel, Steve Fink, GurusamySarathy, Ilya Zakharevich, Benjamin Sugars, Jürgen Christoffel, JoshuaPritikin, and Alan Burlison, for their help in reality-checking andpolishing this article. Big thanks to Tom Christiansen for his rewriteof the prime number generator.

AUTHOR

Dan Sugalski <[email protected]<gt>

Slightly modified by Arthur Bergman to fit the new thread model/module.

Reworked slightly by Jörg Walter <[email protected]<gt> to be more conciseabout thread-safety of Perl code.

Rearranged slightly by Elizabeth Mattijsen <[email protected]<gt> to putless emphasis on yield().

Copyrights

The original version of this article originally appeared in The PerlJournal #10, and is copyright 1998 The Perl Journal. It appears courtesyof Jon Orwant and The Perl Journal. This document may be distributedunder the same terms as Perl itself.

 
Source : perldoc.perl.org - Official documentation for the Perl programming language
Site maintained by Jon Allen (JJ)     See the project page for more details
Documentation maintained by the Perl 5 Porters
(Sebelumnya) Tutorial on pack and unpackTutorial for writing XSUBs (Berikutnya)