Example 1
We're going to request five different urls:
single-threaded
import time import urllib2 defget_responses(): urls=[ ‘', ‘', ‘', ‘', ‘https://' ] start=() forurlinurls: printurl resp=(url) () print”Elapsed time: %s”%(()-start) get_responses()
The output is:
.com200
.com200
.com200
.com200
https://www.jb51.net200
Elapsed time:3.0814409256
Explanation:
The url is requested sequentially
Unless the cpu gets a response from one url, it doesn't go on to request the next url
Network requests can take a long time, so the cpu remains idle for the time it takes to wait for the return of the network request.
multi-threaded
import urllib2 import time from threading import Thread classGetUrlThread(Thread): def__init__(self, url): =url super(GetUrlThread,self).__init__() defrun(self): resp=() , () defget_responses(): urls=[ ‘', ‘', ‘', ‘', ‘https://' ] start=() threads=[] forurlinurls: t=GetUrlThread(url) (t) () fortinthreads: () print”Elapsed time: %s”%(()-start) get_responses()
Output:
https://www.jb51.net200
.com200
.com200
.com200
.com200
Elapsed time:0.689890861511
Explanation:
Aware of the program's improvement in execution time
We have written a multithreaded program to reduce the waiting time of the cpu, when we are waiting for the return of a network request within one thread, at that time the cpu can switch to other threads to carry out the network request within other threads.
We expect one thread to handle one url, so when instantiating the thread class we pass a url.
Thread running means executing the run() method in the class.
In any case we want each thread to have to execute run().
Creating a thread for each url and calling the start() method tells the cpu to execute the run() method in the thread.
We want to count the time spent when all the threads have finished executing, so we call the join() method.
join() can notify the main thread to wait for this thread to finish before the next instruction can be executed.
For each thread we called the join() method, so we are calculating the runtime after all threads have executed.
About the thread:
The cpu may not execute the run() method immediately after calling start().
You can't determine the order in which run() is executed between thread builds.
For a separate thread, it is guaranteed that the statements in the run() method are executed in order.
This is because the in-thread url will be requested first, and then the returned result will be printed.
Example 2
We will use a program to demonstrate resource contention between multiple threads and fix the problem.
from threading import Thread #define a global variable some_var=0 classIncrementThread(Thread): defrun(self): #we want to read a global variable #and then increment it globalsome_var read_value=some_var print”some_var in %s is %d”%(, read_value) some_var=read_value+1 print”some_var in %s after increment is %d”%(, some_var) defuse_increment_thread(): threads=[] foriinrange(50): t=IncrementThread() (t) () fortinthreads: () print”After 50 modifications, some_var should have become 50″ print”After 50 modifications, some_var is %d”%(some_var,) use_increment_thread()
Run this program multiple times and you will see multiple different results.
Explanation:
There is a global variable that all threads want to modify.
All threads should add 1 to this global variable.
There are 50 threads, and at the end this value should become 50, but it doesn't.
Why didn't you reach 50?
Thread t1 reads some_var when some_var is 15, and at this moment the cpu gives control to another thread, t2.
The t2 thread reads some_var also 15
Both t1 and t2 add some_var to 16
At the time, we were expecting two threads t1 t2 to make some_var + 2 into 17
Herein lies the competition for resources.
The same can happen between other threads, so there is a situation where the final result is less than 50.
Addressing competition for resources
from threading import Lock, Thread lock=Lock() some_var=0 classIncrementThread(Thread): defrun(self): #we want to read a global variable #and then increment it globalsome_var () read_value=some_var print”some_var in %s is %d”%(, read_value) some_var=read_value+1 print”some_var in %s after increment is %d”%(, some_var) () defuse_increment_thread(): threads=[] foriinrange(50): t=IncrementThread() (t) () fortinthreads: () print”After 50 modifications, some_var should have become 50″ print”After 50 modifications, some_var is %d”%(some_var,) use_increment_thread()
Running this program again achieved the results we expected.
Explanation:
Lock is used to prevent competitive conditions
If thread t1 acquires a lock before performing some operation. Other threads will not perform the same operation until t1 releases the Lock
What we want to make sure is that once thread t1 has read some_var, no other thread can read some_var until t1 has finished modifying some_var
This way reading and modifying some_var becomes a logical atomic operation.
Example 3
Let's use an example to demonstrate that a thread cannot affect variables (non-global variables) within other threads.
() can make a thread hang and force a thread switch to occur.
from threading import Thread import time classCreateListThread(Thread): defrun(self): =[] foriinrange(10): (1) (i) defuse_create_list_thread(): foriinrange(3): t=CreateListThread() () use_create_list_thread()
After running it a few times I realized that it wasn't printing the results it was striving for. While one thread was printing, the cpu switched to another thread, so incorrect results were produced. We need to make sure that print is a logical atomic operation in case the print is interrupted by another thread.
We used Lock(), see the example below.
from threading import Thread, Lock import time lock=Lock() classCreateListThread(Thread): defrun(self): =[] foriinrange(10): (1) (i) () () defuse_create_list_thread(): foriinrange(3): t=CreateListThread() () use_create_list_thread()
This time we see the correct result. It is proved that a thread cannot modify variables inside other threads (non-global variables).