⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 perlothrtut.pod

📁 视频监控网络部分的协议ddns,的模块的实现代码,请大家大胆指正.
💻 POD
📖 第 1 页 / 共 3 页
字号:
for a thread to exit and extract any scalars it might return, you canuse the join() method.    use Thread;     $thr = Thread->new( \&sub1 );    @ReturnData = $thr->join;     print "Thread returned @ReturnData";     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. detach() is covered later in the article.=head2 Errors In ThreadsSo what happens when an error occurs in a thread? Any errors thatcould be caught with eval() are postponed until the thread isjoined.  If your program never joins, the errors appear when yourprogram exits.Errors deferred until a join() can be caught with eval():    use Thread qw(async);     $thr = async {$b = 3/0};   # Divide by zero error    $foo = eval {$thr->join};     if ($@) {         print "died with error $@\n";     } else {         print "Hey, why aren't you dead?\n";     }eval() passes any results from the joined thread back unmodified, soif you want the return value of the thread, this is your only chanceto get them.=head2 Ignoring A Threadjoin() 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.    use Thread;     $thr = Thread->new( \&sub1 ); # Spawn the thread    $thr->detach; # Now we officially don't care any more    sub sub1 {         $a = 0;         while (1) {             $a++;             print "\$a is $a\n";             sleep 1;         }     }Once a thread is detached, it may not be joined, and any output thatit might have produced (if it was done and waiting for a join) islost.=head1 Threads And DataNow 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.=head2 Shared And Unshared DataThe single most important thing to remember when using threads is thatall threads potentially have access to all the data anywhere in yourprogram.  While this is true with a nonthreaded Perl program as well,it's especially important to remember with a threaded program, sincemore than one thread can be accessing this data at once.Perl's scoping rules don't change because you're using threads.  If asubroutine (or block, in the case of async()) could see a variable ifyou weren't running with threads, it can see it if you are.  This isespecially important for the subroutines that create, and makes C<my>variables even more important.  Remember--if your variables aren'tlexically scoped (declared with C<my>) you're probably sharing thembetween threads.=head2 Thread Pitfall: RacesWhile threads bring a new set of useful tools, they also bring anumber of pitfalls.  One pitfall is the race condition:    use Thread;     $a = 1;     $thr1 = Thread->new(\&sub1);     $thr2 = Thread->new(\&sub2);     sleep 10;     print "$a\n";    sub sub1 { $foo = $a; $a = $foo + 1; }    sub sub2 { $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:    use Thread qw(async);     $a = 2;     async{ $b = $a; $a = $b + 1; };     async{ $c = $a; $a = $c + 1; };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.Whenever your program accesses data or resources that can be accessedby other threads, you must take steps to coordinate access or riskdata corruption and race conditions.=head2 Controlling access: lock()The lock() function takes a variable (or subroutine, but we'll get tothat later) and puts a lock on it.  No other thread may lock thevariable until the locking thread exits the innermost block containingthe lock.  Using lock() is straightforward:    use Thread qw(async);     $a = 4;     $thr1 = async {         $foo = 12;         {             lock ($a); # Block until we get access to $a             $b = $a;             $a = $b * $foo;         }         print "\$foo was $foo\n";    };     $thr2 = async {         $bar = 7;         {             lock ($a); # Block until we can get access to $a            $c = $a;             $a = $c * $bar;         }         print "\$bar was $bar\n";    };     $thr1->join;     $thr2->join;     print "\$a is $a\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 innermost 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.  Locked subroutines behave differently,however.  We'll cover that later in the article.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.Finally, 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.=head2 Thread Pitfall: DeadlocksLocks are a handy tool to synchronize access to data.  Using themproperly is the key to safe shared data.  Unfortunately, locks aren'twithout their dangers.  Consider the following code:    use Thread qw(async yield);     $a = 4;     $b = "foo";     async {         lock($a);         yield;         sleep 20;         lock ($b);     };     async {         lock($b);         yield;         sleep 20;         lock ($a);     };This program will probably hang until you kill it.  The only way itwon't hang is if one of the two async() routines acquires both locksfirst.  A guaranteed-to-hang version is more complicated, but theprinciple is the same.The first thread spawned by async() will grab a lock on $a then, asecond or two later, try to grab a lock on $b.  Meanwhile, the secondthread grabs a lock on $b, then later tries to grab a lock on $a.  Thesecond lock attempt for both threads will block, each waiting for theother 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.=head2 Queues: Passing Data AroundA 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:    use Thread qw(async);     use Thread::Queue;    my $DataQueue = Thread::Queue->new();    $thr = async {         while ($DataElement = $DataQueue->dequeue) {             print "Popped $DataElement off the queue\n";        }     };     $DataQueue->enqueue(12);     $DataQueue->enqueue("A", "B", "C");     sleep 10;     $DataQueue->enqueue(undef);You create the queue with new Thread::Queue.  Then you can add lists ofscalars onto the end with enqueue(), and pop scalars off the front ofit with dequeue().  A queue has no fixed size, and can grow as neededto 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.=head1 Threads And CodeIn addition to providing thread-safe access to data via locks andqueues, threaded Perl also provides general-purpose semaphores forcoarser synchronization than locks provide and thread-safe access toentire subroutines.=head2 Semaphores: Synchronizing Data AccessSemaphores are a kind of generic locking mechanism.  Unlike lock, whichgets a lock on a particular scalar, Perl doesn't associate anyparticular thing with a semaphore so you can use them to controlaccess to anything you like.  In addition, semaphores can allow morethan one thread to access a resource at once, though by defaultsemaphores only allow one thread access at a time.=over 4=item Basic semaphoresSemaphores have two methods, down and up. down decrements the resourcecount, while up increments it.  down calls will block if thesemaphore's current count would decrement below zero.  This programgives a quick demonstration:    use Thread qw(yield);     use Thread::Semaphore;     my $semaphore = Thread::Semaphore->new();    $GlobalVariable = 0;    $thr1 = Thread->new( \&sample_sub, 1 );    $thr2 = Thread->new( \&sample_sub, 2 );    $thr3 = Thread->new( \&sample_sub, 3 );    sub sample_sub {         my $SubNumber = shift @_;         my $TryCount = 10;         my $LocalCopy;         sleep 1;         while ($TryCount--) {             $semaphore->down;             $LocalCopy = $GlobalVariable;             print "$TryCount tries left for sub $SubNumber (\$GlobalVariable is $GlobalVariable)\n";             yield;             sleep 2;             $LocalCopy++;             $GlobalVariable = $LocalCopy;             $semaphore->up;         }     }The three invocations of the subroutine all operate in sync.  Thesemaphore, though, makes sure that only one thread is accessing theglobal variable at once.=item Advanced SemaphoresBy 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. down() decrements thecounter and up() increments the counter.  By default, semaphores arecreated with the counter set to one, down() decrements by one, andup() increments by one.  If down() attempts to decrement the counterbelow zero, it blocks until the counter is large enough.  Note thatwhile a semaphore can be created with a starting count of zero, anyup() or down() always changes the counter by at leastone. $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/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' 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.

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -