We will see some instances of using threads in Python and how to avoid the 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 run out of different results 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 Timeimport urllib2def 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 200http://www.amazon.com 200http://www.ebay.com 200http://www.alibaba.com 200http:// Www.reddit.com 200Elapsed time:3.0814409256
Explain:
The URL order is requested
The next URL is not requested unless the CPU receives a response from a URL
Network requests take a long time, so the CPU is idle for the return time of the network request.
Multithreading
Import urllib2import timefrom Threading Import ThreadClass 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 200http://www.google.com 200http://www.amazon.com 200http://www.alibaba.com 200http:// www.ebay.com 200Elapsed time:0.689890861511
Explain:
Aware of the program's improvement in execution time
We wrote a multithreaded program to reduce CPU wait times, and when we waited for a network request to return within a thread, the CPU could switch to another thread to make a network request within another thread.
We expect a thread to process a URL, so when we instantiate the thread class we pass a URL.
Running a thread means executing the method in the class run() .
Anyway we want each thread to have to execute run() .
Create a thread for each URL and invoke the start() method, which tells the CPU to execute the method in the thread run() .
We want to calculate the time spent when all the threads have finished executing, so we call the join() method.
join()You can tell the main thread to wait for the thread to finish before you can execute the next instruction.
Each thread we call the join() method, so we are calculating the elapsed time after all the threads have finished executing.
About Threads:
The CPU may not start() execute the method immediately after the call run() .
You cannot determine the run() order of execution between different line Chengjian.
For a single thread, the run() statements in the method are guaranteed to be executed sequentially.
This is because the URL inside the thread is requested first, and then the returned result is printed out.
Example 2
We will use a program to demonstrate the resource competition between multiple threads and fix this problem.
From threading import Thread#define a global Variablesome_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 was%d "% (Self.name, read_value) Some_var = read_value + 1 print" Some_var in%s after 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 "After modifications, some_ var should has become " print" after the modifications, Some_var is%d "% (Some_var,) Use_increment_thread ()
Run this program multiple times and you will 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 doesn't.
Why not reach 50?
At some_var that 15 time, the thread t1 reads some_var , and at this point the CPU gives control to the other thread t2 .
t2The thread reads the some_var same15
t1and t2 all some_var add to16
At the time we were expecting t1 t2 two threads to some_var + 2 turn into a17
There is a competition for resources here.
The same situation can occur between other threads, so there is a case where the final result is less 50 .
Resolving resource Competition
From threading import Lock, Threadlock = Lock () Some_var = 0 Class Incrementthread (Thread): def run: #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 was%d"% (Self.name, read_value) Some_var = read_value + 1 print "Some_var in%s after Inc Rement is%d "% (Self.name, Some_var) lock.release () def use_increment_thread (): threads = [] for I in range (a): t = incrementthread () threads.append (t) T.start () for T in Threads: t.join () Print "After modifications, Some_var should has become" print "after modifications, Some_var is%d"% (some _var,) Use_increment_thread ()
Run the program again to achieve the results we expect.
Explain:
Lock is used to prevent competitive conditions
If a thread obtains a lock before performing some action t1 . Other threads do t1 not perform the same operation until Lock is released
What we want to make sure is that once the thread t1 has been read some_var , the t1 some_var other threads can read it until the modification is complete.some_var
This reads and modifies the some_var logical atomic operation.
Example 3
Let's use an example to prove that a thread cannot affect variables within other threads (non-global variables).
Time.sleep () can cause a thread to hang and force a thread switch to occur.
From threading Import Threadimport Timeclass Createlistthread (Thread): def run (self): self.entries = [] For I in range: time.sleep (1) self.entries.append (i) print self.entriesdef Use_create_list_ Thread (): For I in range (3): t = createlistthread () T.start () Use_create_list_thread ()
Run a few times and find that the results are not printed out. When a line is impersonating in print, the CPU switches to another thread, resulting in incorrect results. We need to make sure that print self.entries it's a logical atomic operation to avoid being interrupted by other threads while printing.
We used lock () to see the example below.
From threading import Thread, Lockimport timelock = Lock () class Createlistthread (Thread): def run (self): Self.entries = [] for i in range: 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 see the right results. proves that a thread can not modify variables (non-global variables) inside other threads.
Original source: Akshar Raaj