Thread: A scan that uses multithreading to handle large amounts of data and encounters a possible deadlock in read-write files.
The life cycle of a Perl thread 1. Use the Create () method of the threads package:
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");
2.join and detach methods
Once the thread is successfully created, it immediately starts running. At this time, you are faced with two choices, namely join or detach. Of course you can do nothing, but this is not a good habit.
Literally, join is to combine the newly created thread into the current main thread, treat it as part of the main thread, and merge them into one. Join will trigger two actions. First, the main thread will request the return value after the execution of the newly created thread is finished. Second, the newly created thread will automatically release its own system resources after the execution is completed and the result is returned. E.g
join harvesting 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 out that the timing of calling join is a very interesting problem. If the join method is called too early and the newly created thread has not finished executing, it will naturally not return any results. At this time, the main thread will have to be blocked, and the return value will not be obtained until the newly created thread is executed. The join can end, which largely breaks the parallelism between threads. On the contrary, if the join method is called too late, the newly created thread has already been executed, and since there is no chance to return the result, the resources it occupies cannot be released until it is joined, which largely wastes valuable system resources. . Therefore, the best time to join a newly created thread is when it has just finished executing, so that it will not block the execution of the current thread and release the system resources occupied by the newly created thread in time.
foreach (threads-> list (threads :: joinable)) {
$ _-> join ();
}
Let's take a look at the detach method again. This is probably the most worry-free method. Literally, detach is to detach the newly created thread from the current main thread, making it irrelevant to the main thread. When you use the detach method, it indicates that the main thread does not care about the results returned after the new thread is executed. Perl will automatically release the resources it occupied after the new thread is executed.
detach
#! / 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);
Once a newly created thread is detached, it can no longer join. When you use the detach method to strip threads, one thing that needs special attention is that you need to ensure that the created thread ends before the main thread, otherwise the thread you created will be forced to terminate, unless this result is exactly what you want Otherwise, this may cause abnormal situations and increase the difficulty of program debugging.
3. The demise of threads
In most cases, you want the thread you created to exit normally, which means that the function body corresponding to the thread returns and releases resources after execution is complete. For example, in the example in Listing 5, the exit process after the newly created thread is joined. However, if due to improper detach or due to some unexpected exceptions in the main line, the threads created by it may not be executed yet, but they will still be forcibly terminated. As long as there is no such thing, Mao will cling. You may get a warning similar to "Perl exited with active threads".
Of course, you can also call the exit () method to end a thread explicitly, but it is worth noting that by default, if you call the exit () method in one thread, other threads will end with it, in many cases In this case, this may not be what you want. If you want the exit () method to take effect only in the thread that called it, then you need to set 'exit' => 'thread_only' when creating the thread. E.g
Set the exit 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 effective only for you, then you must explicitly set the 'exit' => 'thread_only' property every time you create a new thread. You can also introduce threads Setting this property when package is valid globally, for example
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
#! / 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 ();
Deadlock is often the most hidden problem in multi-threaded programs. It is often difficult to find and debug, and it also increases the difficulty of troubleshooting. In order to avoid the problem of deadlock in the program, we should try to avoid acquiring locks of multiple shared variables at the same time in the program. If it cannot be avoided, then one must try to use the same order to acquire locks of multiple shared variables. The granularity of locking should be as fine as possible, and the locking time should be reduced.
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
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
}
Semaphore: Thread :: Semaphore
my $ semaphore = Thread :: Semaphore-> new ($ max_threads); #semaphore
my $ mutex = Thread :: Semaphore-> new (1); #mutex
the difference:
1. Mutexes are used for mutual exclusion of threads, and semaphores are used for synchronization of threads.
This is the fundamental difference between mutexes and semaphores, that is, the difference between mutexes and synchronization.
Mutual exclusion: refers to a resource that allows only one visitor to access it at the same time, which is unique and exclusive. But mutual exclusion cannot restrict the order in which visitors access resources, that is, the access is out of order.
Sync: refers to On the basis of mutual exclusion (in most cases), orderly access to resources by visitors through other mechanisms. In most cases, synchronization has achieved mutual exclusion, especially in the case of all write resources must be mutually exclusive. In rare cases, it is possible to allow multiple visitors to access a resource at the same time
The above differences are mainly to remember.
note: A semaphore can be used to implement the function of a mutex
2. The mutex value can only be 0/1, and the semaphore value can be a non-negative integer.
In other words, a mutex can only be used for mutually exclusive access to a resource, it cannot achieve the multi-thread mutual exclusion of multiple resources. A semaphore can achieve multi-thread mutual exclusion and synchronization of multiple similar resources. When the semaphore is a single-valued semaphore, a mutually exclusive access to a resource can also be completed.
3. The locking and unlocking of a mutex must be used by the same thread respectively. The semaphore can be released by one thread and obtained by another thread.
use threads;
use threads :: shared;
use Thread :: Semaphore;
my $ s = Thread :: Semaphore-> new ();
$ s-> down (); # P operation
...
$ s-> up (); # V operation
Thread :: Queue: The producer-consumer model uses multi-threaded queues.
The producer can continuously do enqueue operations on the thread queue, and the consumer only needs to do dequeue operations on the thread queue continuously, which easily achieves the synchronization between the producer and the consumer.
#! / 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 ();
perl multithreaded understanding