We'll see some examples of how threads are used in Python and how to avoid competition between threads. You should run the example below several times so that you can notice that the thread is unpredictable and that the threads are running differently each time. Disclaimer: Start by forgetting what you've heard about Gil, because Gil doesn't affect what I want to show.
Example 1
We are going to request five different URLs:
Single Thread
Import time
import URLLIB2
def get_responses ():
urls = [
' http://www.google.com ',
' http:// www.amazon.com ',
' http://www.ebay.com ',
' http://www.alibaba.com ',
' http://www.reddit.com '
start = Time.time () for
URL in URLs:
print url
resp = urllib2.urlopen (URL)
print Resp.getcode ()
print "Elapsed time:%s"% (Time.time ()-start)
get_responses ()
The output is:
http://www.google.com
http://www.amazon.com
http://www.ebay.com
http://www.alibaba.com
http://www.reddit.com
Elapsed time:3.0814409256
Explain:
- URL Order is requested
- The next URL is not requested unless the CPU receives a response from a URL
- The network request takes a long time, so the CPU is idle for the return time of the network request.
Multithreading
Import urllib2
import time from
threading import Thread
class Geturlthread (thread):
def __init__ (self , URL):
self.url = URL
super (Geturlthread, self). __init__ ()
def run (self):
resp = Urllib2.urlopen ( Self.url)
print Self.url, Resp.getcode ()
def get_responses ():
urls = [
' http://www.google.com ',
' http://www.amazon.com ',
' http://www.ebay.com ', ' http://www.alibaba.com ', '
http:// Www.reddit.com '
]
start = Time.time ()
threads = [] for
URL in urls:
t = geturlthread (URL)
threads.append (t)
T.start () for
T in Threads:
t.join ()
print "Elapsed time:%s"% (time.time ()-start)
get_responses ()
Output:
http://www.reddit.com
http://www.google.com
http://www.amazon.com
http://www.alibaba.com
http://www.ebay.com
Elapsed time:0.689890861511
Explain:
- Aware of the improvement in the execution time of the program
- We wrote a multithreaded program to reduce CPU latency, and when we wait for a network request within a thread to return, the CPU can switch to other threads to make network requests in other threads.
- We expect a thread to process a URL, so we pass a URL when instantiating the thread class.
- Running a thread means executing the run () method in the class.
- Anyway we think each thread must execute run ().
- Create a thread for each URL and call the start () method, which tells the CPU to execute the run () method in the thread.
- We want all threads to compute the time spent when they finish, so we call the join () method.
- Join () can notify the main thread to wait for the thread to complete before executing the next instruction.
- We call the join () method on each thread, so we calculate the elapsed time after all threads have finished executing.
About Threads:
- The CPU may not execute the run () method immediately after calling start ().
- You cannot determine the order in which run () is executed between different line Chengjian.
- For a single thread, you can guarantee that the statements in the run () method are executed in order.
- This is because the URLs within the thread are first requested and then printed out as the result of the return.
Instance 2
We will use a program to demonstrate the resource competition between multithreading and fix the problem.
From threading import Thread
#define A global variable
some_var = 0
class Incrementthread (Thread):
def Run (self):
#we want to read a global variable
#and then increment it
global some_var
read_value = Some_var
print ' Some_var in%s ' is%d '% (Self.name, read_value)
Some_var = read_value + 1
print "Some_var in%s afte R increment is%d '% (Self.name, Some_var)
def use_increment_thread ():
threads = [] for
I in range ():
t = Incrementthread ()
threads.append (t)
T.start () for
T in Threads:
t.join ()
Print Modifications, Some_var should have become "
print" after modifications, Some_var is%d "% (Some_var,)
u Se_increment_thread ()
Run this program multiple times and you'll see a variety of different results.
Explain:
- There is a global variable, and all threads want to modify it.
- All threads should add 1 to this global variable.
- There are 50 threads, and the last value should be 50, but it's not.
Why not reach 50?
- When the Some_var is 15, the thread T1 reads the Some_var, at which point the CPU gives control to another thread t2.
- T2 thread Read the Some_var is also 15
- T1 and T2 are adding Some_var to 16.
- What we expected at the time was T1 T2 two threads to make Some_var + 2 into 17
- There is competition for resources here.
- The same situation may also occur between other threads, so the final result is less than 50.
Resolving resource Competition
From threading import Lock, Thread
Lock = Lock ()
Some_var = 0
class Incrementthread (Thread):
def run ( Self):
#we want to read a global variable
#and then increment it
global some_var
lock.acquire ()
read _value = Some_var
print "Some_var in%s is%d" (Self.name, read_value)
Some_var = read_value + 1
print "s Ome_var in%s after increment is%d "% (Self.name, Some_var)
lock.release ()
def use_increment_thread ():
threads = [] for
I in range (x):
t = incrementthread ()
threads.append (t)
T.start () for
T in Threads:
t.join ()
print "After modifications, Some_var should have-become"
print "after Modificat Ions, Some_var is%d "% (Some_var,)
Use_increment_thread ()
Run this program again to achieve the results we expected.
Explain:
- Lock used to prevent competitive conditions
- If the thread T1 acquired the lock before performing some action. Other threads do not perform the same action until T1 releases lock
- What we want to make sure is that once the thread T1 has read the Some_var, until T1 has finished modifying Some_var, the other threads can read the Some_var
- This reads and modifies the some_var into a logical atomic operation.
Instance 3
Let's use an example to prove that a thread cannot affect a variable in another thread (a non global variable).
Time.sleep () enables a thread to suspend, forcing a thread switch to occur.
From threading import Thread
Import Time
class Createlistthread (thread):
def run (self):
self.entries = [] for
i in range:
time.sleep (1)
self.entries.append (i)
print self.entries
def use_ Create_list_thread (): For
I in range (3):
t = createlistthread ()
T.start ()
use_create_list_ Thread ()
Several times after running, the results were not printed out. When a line one thread is printed, the CPU switches to another thread, resulting in incorrect results. We need to make sure that the print self.entries is a logical atomic operation to prevent other threads from interrupting when printing.
We used the lock () to see the example below.
From threading import Thread, lock
import time
lock = Lock ()
class Createlistthread (Thread):
def run ( Self):
self.entries = [] for
I in Range (a):
time.sleep (1)
self.entries.append (i)
Lock.acquire (
Print self.entries
lock.release ()
def use_create_list_thread (): For
I in range (3):
t = Createlistthread ()
T.start ()
Use_create_list_thread ()
This time we saw the right result. proves that a thread cannot modify a variable (a non global variable) inside another thread.