Threads in Perl

Source: Internet
Author: User
Tags exit in scalar

Thread Overview

A thread is a single execution process. It is the smallest control unit in the execution process of all programs.
The minimum task unit to schedule. There are both connections and completely different relations between threads and processes. Simply put, a thread must belong to a certain process, and a process contains at least one or more threads. Early computers
The system can only run one program at a time. Therefore, when multiple programs need to be executed, the only way is to queue them and execute them in sequence. The appearance of the process breaks this pattern, CPU
Resources are separated by time slice and allocated to different processes for use. In this way, although the execution of processes is still serial in a micro view, different programs are already executed in parallel in a macro view. If I
We apply the same idea to the process. Naturally, the process will be subdivided into smaller execution units, namely, threads. Because a process often needs to execute multiple similar tasks at the same time, these subdivided threads
The same Code segment, data segment, file handle, and other resources can be shared between them. With the process, we can run both Firefox and
Microsoft Office Word and other programs; with threads, we can make Firefox load multiple different pages in different labels at the same time.
Edit the document in Office Word and check for syntax errors. Therefore, threads bring us a higher CPU
Utilization, faster program response, more economical resource usage, and better adaptability to the multi-CPU architecture.

 

History of Perl threads

5005 threads thread model

Perl's support for threads can be traced back to Perl v5.005, which was released in July 1998. Its release statement states that Perl v5.005
Added support for operating system-level threads. This new feature is an experimental product. This is what we call the 5005 threads thread model. For
In the 5005 threads thread model, by default, all data structures are shared, so you must be responsible for Synchronous access to these shared data structures. Today
5005 threads is no longer recommended for use. In Versions later than Perl v5.10, The 5005 threads thread model will no longer be supported.

Ithreads thread model

A new thread model, interpreter threads, was introduced in Perl v5.6.0 released in May 2000,
It is also the first time that 5005 threads was proposed in the release statement of this version.
The thread model may be disabled in the future. Even so, ithreads was a new experimental thread model at that time, and users could not use it directly. The only way was through
Fork function simulation. After two years of development, Perl v5.8.0 was officially released in July 2002. At this time, ithreads
It is already a relatively mature thread model. In the release statement, users are also encouraged to switch from the old 5005 threads thread model to the new ithreads thread model.
5005 threads thread model will be eliminated. All the content discussed later in this article is based on the new ithreads thread model. In ithreads
In the thread model, the most distinctive feature is that by default, all data structures are not shared, which we will have a deeper understanding in the following content.

Which thread model does the existing environment support?

Since there may be two different thread models in Perl, we naturally need to determine which thread implementation method is supported by the current Perl environment. To sum up, we have two methods:

  1. In Shell, we can obtain information about the current thread model by executing the perl-v | grep usethreads command, such


Listing 1. querying the current Perl thread model in Shell

				
 > perl -V | grep use.*threads 
 config_args='-des -Doptimize=-O2 -g -pipe -m32 -march=i386 -mtune=pentium4 -Dversion=5.8.5 
 -Dmyhostname=localhost -Dperladmin=root@localhost -Dcc=gcc -Dcf_by=Red Hat, Inc. 
 -Dinstallprefix=/usr -Dprefix=/usr -Darchname=i386-linux -Dvendorprefix=/usr 
 -Dsiteprefix=/usr -Duseshrplib -Dusethreads -Duseithreads -Duselargefiles -Dd_dosuid 
 -Dd_semctl_semun -Di_db -Ui_ndbm -Di_gdbm -Di_shadow -Di_syslog -Dman3ext=3pm -Duseperlio 
 -Dinstallusrbinperl -Ubincompat5005 -Uversiononly -Dpager=/usr/bin/less -isr 
 -Dinc_version_list=5.8.4 5.8.3 5.8.2 5.8.1 5.8.0'
 usethreads=define use5005threads=undef useithreads=define usemultiplicity=define 

 

The results show that the ithreads thread model is supported in the current Perl environment.

  1. In a Perl program, we can also use the config module to dynamically obtain information about the Perl thread model, such


Listing 2. dynamically obtaining the current Perl thread model in the Perl Program

 #!/usr/bin/perl 
 # 
 use Config; 

 if( $Config{useithreads} ) { 
 printf("Hello ithreads/n") 
 } 
 elsif( $Config{use5005threads} ) { 
 printf("Hello 5005threads/n"); 
 } 
 else { 
 printf("Can not support thread in your perl environment/n"); 
    exit( 1 ); 
 } 

 

It is worth mentioning that for the 5005 threads and ithreads thread models, Perl can only support one of them. You cannot use these two thread models in a Perl environment at the same time. All the content discussed later in this article is based on the ithreads thread model.

 

 

Lifecycle of a Perl thread

Create thread

Thread as Perl
An entity in a lifetime can be roughly divided into three stages: creation, operation, and exit. When a thread is created from scratch, running is the stage in which the thread completes its main work. exit is the termination of the thread.
The running of a thread is very similar to that of a common function. It has its entry parameters, a specific code flow, and one or more results returned after execution, the only difference between a function call and a common function call is that a new line is created.
The execution of the program is parallel to the execution of the current thread.

Creating a New thread in Perl is very simple. There are two main methods:

  1. Use the CREATE () method of the threads package, for example


Listing 3. Using the CREATE () method to create a thread

 use threads; 

 sub say_hello 
 { 
    printf("Hello thread! @_./n"); 
 return( rand(10) ); 
 } 

 my $t1 = threads->create( /&say_hello, "param1", "param2" ); 
 my $t2 = threads->create( "say_hello", "param3", "param4" ); 
 my $t3 = threads->create( 
 sub { 
 printf("Hello thread! @_/n"); 
                 return( rand(10) ); 
 }, 
 "param5", 
             "param6" ); 

 

  1. Use the async {} block to create a thread, for example


Listing 4. Creating a thread through async {}

 #!/usr/bin/perl 
 # 
 use threads; 

 my $t4 = async{ 
 printf("Hello thread!/n"); 
         }; 

 

Join method and detach Method

Once a thread is successfully created, it starts to run immediately. At this time, you are faced with two options: Join or detach. You can also do nothing, but this is not a good habit. We will explain why it is later.

Let's take a look at the join method. This may be what you want in most cases. Literally speaking, join
It is to combine the newly created thread into the current main thread and treat it as a part of the main thread so that they can be combined into one. Join
Two actions are triggered. First, the main thread requests the return value after the execution of the newly created thread ends. Second, after the new thread is executed and the result is returned, the system resources occupied by it are automatically released. For example


Listing 5. Using the join () method to harvest new threads

 #!/usr/bin/perl 
 # 
 use threads; 

 sub func { 
 sleep(1); 
 return(rand(10)); 
 } 

 my $t1 = threads->create( /&func ); 
 my $t2 = threads->create( /&func ); 

 printf("do something in the main thread/n"); 

 my $t1_res = $t1->join(); 
 my $t2_res = $t2->join(); 

 printf("t1_res = $t1_res/nt2_res = $t2_res/n"); 

 

It is not difficult to find that the time to call join is a very interesting problem. If join is called
The method is too early. If the new thread has not been executed, no results will be returned. At this time, the main thread will have to be blocked until the new thread is executed, then the resource will
The join operation can end when it is released, which largely breaks through the parallelism between threads. Conversely, if you call join
The method is too late, and the new thread has already been executed. Because there is no chance to return the result, the resources it occupies cannot be released until it is joined.
So far, this wastes a lot of valuable system resources. Therefore, join
The best time to create a thread is when it is just executed. This will not block the execution of the current thread, but will release the system resources occupied by the new thread in time.

Let's take a look at the detach method, which may be the most effort-saving solution. Literally, detach
The newly created thread is separated from the current main thread so that it is irrelevant to the main thread. When you use detach
Method indicates that the main thread does not care about the results returned after the new thread is executed. After the new thread is executed, Perl Automatically releases the resources it occupies. For example


Listing 6. Using the detach () method to strip a thread

 #!/usr/bin/perl 
 # 
 use threads; 
 use Config; 

 sub say_hello { 
 my ( $name ) = @_; 
 printf("Hello World! I am $name./n"); 
 } 

 my $t1 = threads->create( /&say_hello, "Alex" ); 
 $t1->detach(); 

 printf("doing something in main thread/n"); 
 sleep(1); 

 

After a new thread is detach, it cannot join any more. When you use detach
Note that you must ensure that the created thread ends before the main thread. Otherwise, the created thread is forced to end, unless this result is exactly what you want
It may cause exceptions and increase the difficulty of program debugging.

At the beginning of this section, we mentioned that after a new thread is created, it is not a good habit to neither join nor detach, unless detach is explicitly called.
Method strip the thread, Perl will think that you may need to call it at a certain time point in the future
Therefore, the return value of the newly created thread will be stored in the memory for emergency purposes, and the system resources it occupies will not be released. However, in fact, you do not intend to do anything, so it is valuable.
System resources are released until the end of the entire Perl application. At the same time, since you have not called JOIN or called detach, Perl
A warning is returned for abnormal thread termination.

Thread extinction

In most cases, you want the created thread to exit normally, which means that the function body corresponding to the thread returns and releases resources after execution. For example, in listing 5
In the example, the exit process after the new thread is joined. However
Improper or because the main line ends early due to some unexpected exceptions, even though the threads it creates may not have been completed, they will still be forced to stop. This is what we call the "hidden" feature. Then you may
Get a warning similar to "Perl exited with active Threads.

Of course, you can also call the exit () method explicitly to end a thread. However, it is worth noting that, by default, if you call exit () in a thread ()
And other threads will end together. In many cases, this may not be what you want. If you want to exit ()
The method takes effect only within the thread that calls it. When creating this thread, you need to set 'exit '=> 'thread_only '. For example


Listing 7. Setting the 'exit '=> 'thread_only' attribute for a thread

				
 #!/usr/bin/perl 
 # 
 use threads; 

 sub say_hello { 
 printf("Hello thread! @_./n"); 
 sleep(10); 
 printf("Bye/n"); 
 } 

 sub quick_exit { 
 printf("I will be exit in no time/n"); 
 exit(1); 
 } 

 my $t1 = threads->create( /&say_hello, "param1", "param2" ); 
 my $t2 = threads->create( {'exit'=>'thread_only'}, /&quick_exit ); 

 $t1->join(); 
 $t2->join(); 

 

If you want the exit method of each thread to be valid only for yourself, it is obviously troublesome to explicitly set the 'exit '=> 'thread_only' attribute every time you create a new thread, you can also set this attribute to be valid globally when introducing the threads package, for example


Listing 8. Set 'exit '=> 'thread_only' to a global attribute.

 use threads ('exit' => 'threads_only'); 

 sub func { 
 ... 
 if( $condition ) { 
 exit(1); 
 } 
 } 

 my $t1 = threads->create( /&func ); 
 my $t2 = threads->create( /&func ); 

 $t1->join(); 
 $t2->join(); 

 


Sharing and Synchronization

Threads: shared

Unlike most existing thread models
In the thread model, no data structure is shared by default. After a new thread is created, it contains a private copy of all the current data structures.
Any operation of the structure will not be valid in other threads. Therefore, any shared data must be explicitly stated. Threads: shared
Packages can be used to share data between threads.


Listing 9. applying and using shared data in the thread

				
 #!/usr/bin/perl 
 # 

 use threads; 
 use threads::shared; 
 use strict; 

 my $var   :shared  = 0;       # use :share tag to define 
 my @array :shared = (); # use :share tag to define 
 my %hash = (); 
 share(%hash);                  # use share() funtion to define 


 sub start { 
 $var = 100; 

 @array[0] = 200; 
 @array[1] = 201; 

 $hash{'1'} = 301; 
 $hash{'2'} = 302; 
 } 

 sub verify { 
    sleep(1);                      # make sure thread t1 execute firstly 
    printf("var = $var/n");     # var=100 

 for(my $i = 0; $i < scalar(@array); $i++) { 
        printf("array[$i] = $array[$i]/n");    # array[0]=200; array[1]=201 
 } 

 foreach my $key ( sort( keys(%hash) ) ) { 
 printf("hash{$key} = $hash{$key}/n"); # hash{1}=301; hash{2}=302 
 } 
 } 

 my $t1 = threads->create( /&start ); 
 my $t2 = threads->create( /&verify ); 

 $t1->join(); 
 $t2->join(); 

 

Lock

Since data is shared among multiple threads, you must carefully access the shared data. Otherwise, conflicts are inevitable. Built-in Perl ithreads thread model
The lock method implements the lock mechanism for data sharing between threads. Interestingly, there is no unlock Method for explicit unlocking. the lifecycle of a lock is measured in code blocks. That is to say, when
After the lock operation is executed, the lock is implicitly released. For example


Listing 10. Lock Mechanism in the thread

				
 use threads::shared; 

 # in thread 1 
 { 
    lock( $share );        # lock for 3 seconds 
    sleep(3);               # other threads can not lock again 
 } 
 # unlock implicitly now after the block 

 # in thread 2 
 { 
    lock($share);          # will be blocked, as already locked by thread 1 
    $share++;               # after thread 1 quit from the block 
 } 
 # unlock implicitly now after the block 

 

In the above example, we use the lock method in thread 1 to lock a common scalar, which causes thread 2 to try to obtain
$ Share variable lock is blocked. When thread 1 exits from the code block that calls lock, the lock is implicitly released, so that thread 2
The blocking ends. After the lock is successful, thread 2 can perform the $ share ++ operation. For arrays and hash tables, lock
It must be used in the entire data structure, rather than an array or hash table element. For example


Listing 11. Using the lock mechanism in an array or hash table

 use threads; 
 use threads::shared; 

 { 
    lock(@share);          # the array has been locked 
    lock(%hash);           # the hash has been locked 
    sleep(3);               # other threads can not lock again 
 } 

 { 
    lock($share[1]);     # error will occur 
    lock($hash{key});    # error will occur 
 } 

 

If a thread implements the lock operation on a shared variable, and before it releases the lock, if another thread also implements the lock operation on the shared variable, the thread will be blocked, and the blocking will not be automatically aborted, but will not be stopped until the previous thread releases the lock. This mode brings us a common deadlock problem. For example


List 12. deadlocks in the thread

				
     
				
 use threads; 
 use threads::shared; 
 # in thread 1 
 { 
    lock($a);              # lock for 3 seconds 
    sleep(3);              # other threads can not lock again 
    lock($b);              # dead lock here 
 } 

 # in thread 2 
 { 
    lock($b);              # will be blocked, as already locked by thread 1 
    sleep(3);              # after thread 1 quit from the block 
    lock($a);              # dead lock here 
 } 

 

Deadlocks are often the most concealed problems in multi-threaded programs, which are difficult to detect and debug and increase the difficulty of troubleshooting. To avoid deadlocks in the program, we should avoid them as much as possible in the program.
Obtain the Lock of multiple shared variables at the same time. If it cannot be avoided, first, try to use the same sequence to obtain the Lock of multiple shared variables, in addition, we should try to refine the lock granularity to reduce the lock time.

Semaphores

Thread: The semaphore package provides semaphore support for threads. You can create a semaphore of your own and use the down operation and up
Operation to Achieve Synchronous access to resources. In fact, the down and up operations correspond to the well-known P operations and V
Operation. From the perspective of internal implementation, thread: semaphore
In essence, it is the shared variable with the lock. It is nothing more than encapsulating the shared variable with the lock into a thread-safe package. Because semaphores do not have to be bound to any variable, they are flexible and can be used to control
Create any data structures and program behaviors you want to synchronize. For example


Listing 13. semaphores in a thread

				
     
				
 use threads; 
 use threads::shared; 
 use Thread::Semaphore; 

 my $s = Thread::Semaphore->new(); 
 $s->down();                # P operation 
 ... 
 $s->up();                  # V operation 

 

Essentially, semaphores are a reference to a shared integer variable. By default, its initial value is 1. The down operation reduces its value by 1, and the Up Operation adds its value to 1. Of course, you can also customize the initial semaphores and the changes of semaphores during each up or down operation. For example


Listing 14. semaphores in a thread

				
 use threads; 
 use Thread::Semaphore; 

 my $s = Thread::Semaphore->new(5); 
 printf("s = " . ${$s} . "/n");         # s = 5 
 $s->down(3); 
 printf("s = " . ${$s} . "/n");         # s = 2 
 ... 
 $s->up(4); 
 printf("s = " . ${$s} . "/n");         # s = 6 

 

Thread queue

Thread: The queue package provides thread-safe queue support for threads. Similar to semaphores, in terms of internal implementation, thread: queue
It also encapsulates a shared queue for Synchronous access through the lock mechanism into a thread-safe package and provides a unified interface for use. Thread: queue
In some cases, the difficulty and cost of Inter-thread communication can be greatly simplified. For example, in the producer-consumer model, the producer can continuously enqueue the thread queue.
The consumer only needs to constantly perform the dequeue operation on the thread queue, which is very simple to implement synchronization between the producer and the consumer. For example


Listing 15. Use of thread queues in the producer-consumer model

				
 #!/usr/bin/perl 
 # 

 use threads; 
 use Thread::Queue; 

 my $q = Thread::Queue->new(); 

 sub produce { 
    my $name = shift; 
    while(1) { 
        my $r = int(rand(100)); 
        $q->enqueue($r); 
        printf("$name produce $r/n"); 
        sleep(int(rand(3))); 
    } 
 } 

 sub consume { 
    my $name = shift; 
    while(my $r = $q->dequeue()) { 
        printf("consume $r/n"); 
    } 
 } 

 my $producer1 = threads->create(/&produce, "producer1"); 
 my $producer2 = threads->create(/&produce, "producer2"); 
 my $consumer1 = threads->create(/&consume, "consumer2"); 

 $producer1->join(); 
 $producer2->join(); 
 $consumer1->join(); 

 


Other useful non-core packages

All the content discussed earlier in this article is within the scope of the Perl thread core package. In fact, CPAN also has some other non-core packages related to threads. They often bring great convenience to the use of Perl threads. Here we will give you a few introductions.

Thread: The pool package allows you to create a batch of threads in the program to complete multiple similar tasks. For example, if you want to create a multi-threaded program to check whether 1000 IP addresses can be pinged, the thread: pool package can help you.

Thread: The rwlock package provides the lock mechanism for read/write operations in the thread. For example, if you have multiple reader and writer threads to access one or more files, the thread: rwlock package can facilitate you.

 

 

Summary

This article mainly introduces Perl
Thread usage, including thread creation, execution, and extinction, how to use shared variables in the thread and implement synchronization between threads through the locking mechanism, semaphore, and thread queue. Perl
The biggest difference between the ithreads thread model and mainstream thread model is that by default, any data structure is not shared, or ithreads in Perl
Is a "non-lightweight" thread model. Although such a thread model increases program overhead, it does not compromise the functionality of the thread. It also makes the communication and sharing between threads easier.
This also conforms to the simple and powerful concepts and principles of Perl.

 

From: http://www.ibm.com/developerworks/cn/linux/l-cn-perl-thread/index.html

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.