我们将会看到一些在Python中运用线程的实例和怎样防备线程之间的合作。
你应当将下边的例子运转屡次,以便能够注意到线程是不可展望的和线程每次运转出的差别效果。
声明:从这里最先忘记你听到过的关于GIL的东西,由于GIL不会影响到我想要展现的东西。
实例1
我们将要要求五个差别的url:
单线程
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()
输出:
http://www.google.com 200
http://www.amazon.com 200
http://www.ebay.com 200
http://www.alibaba.com 200
http://www.reddit.com 200
Elapsed time: 3.0814409256
诠释:
- url递次的被要求
- 除非cpu从一个url获得了回应,不然不会去要求下一个url
- 收集要求会消费较长的时候,所以cpu在守候收集要求的返回时候内一向处于闲置状况。
多线程
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()
输出:
http://www.reddit.com 200
http://www.google.com 200
http://www.amazon.com 200
http://www.alibaba.com 200
http://www.ebay.com 200
Elapsed time: 0.689890861511
诠释:
- 认识到了顺序在实行时候上的提拔
- 我们写了一个多线程顺序来削减cpu的守候时候,当我们在守候一个线程内的收集要求返回时,这时候cpu能够切换到其他线程去举行其他线程内的收集要求。
- 我们希冀一个线程处置惩罚一个url,所以实例化线程类的时候我们传了一个url。
- 线程运转意味着实行类里的run()要领。
- 无论怎样我们想每一个线程必需实行run()。
- 为每一个url建立一个线程而且挪用start()要领,这关照了cpu能够实行线程中的run()要领了。
- 我们愿望一切的线程实行终了的时候再盘算消费的时候,所以挪用了join()要领。
- join()能够关照主线程守候这个线程完毕后,才能够实行下一条指令。
- 每一个线程我们都挪用了join()要领,所以我们是在一切线程实行终了后盘算的运转时候。
关于线程:
- cpu能够不会在挪用start()后立时实行run()要领。
- 你不能肯定run()在差别线程建间的实行递次。
- 关于零丁的一个线程,能够保证run()要领里的语句是根据递次实行的。
- 这就是由于线程内的url会起首被要求,然后打印出返回的效果。
实例2
我们将会用一个顺序演示一下多线程间的资本合作,并修复这个题目。
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 after increment is %d" % (self.name, some_var)
def use_increment_thread():
threads = []
for i in range(50):
t = IncrementThread()
threads.append(t)
t.start()
for t in threads:
t.join()
print "After 50 modifications, some_var should have become 50"
print "After 50 modifications, some_var is %d" % (some_var,)
use_increment_thread()
屡次运转这个顺序,你会看到多种差别的效果。
诠释:
- 有一个全局变量,一切的线程都想修正它。
- 一切的线程应当在这个全局变量上加 1 。
- 有50个线程,末了这个数值应当变成50,然则它却没有。
为何没有到达50?
- 在some_var是15的时候,线程t1读取了some_var,这个时候cpu将控制权给了另一个线程t2。
- t2线程读到的some_var也是15
- t1和t2都把some_var加到16
- 当时我们希冀的是t1、t2两个线程使some_var + 2变成17
- 在这里就有了资本合作。
- 雷同的状况也能够发作在别的的线程间,所以涌现了末了的效果小于50的状况。
处理资本合作
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 "some_var in %s after increment is %d" % (self.name, some_var)
lock.release()
def use_increment_thread():
threads = []
for i in range(50):
t = IncrementThread()
threads.append(t)
t.start()
for t in threads:
t.join()
print "After 50 modifications, some_var should have become 50"
print "After 50 modifications, some_var is %d" % (some_var,)
use_increment_thread()
再次运转这个顺序,到达了我们预期的效果。
诠释:
- Lock 用来防备合作前提
- 如果在实行一些操纵之前,线程t1获得了锁。其他的线程在t1开释Lock之前,不会实行雷同的操纵
- 我们想要肯定的是一旦线程t1已读取了some_var,直到t1完成了修正some_var,其他的线程才能够读取some_var
- 如许读取和修正some_var成了逻辑上的原子操纵。
实例3
让我们用一个例子来证实一个线程不能影响其他线程内的变量(非全局变量)。
time.sleep()能够使一个线程挂起,强迫线程切换发作。
from threading import Thread
import time
class CreateListThread(Thread):
def run(self):
self.entries = []
for i in range(10):
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()
运转频频后发明并没有打印出争夺的效果。当一个线程正在打印的时候,cpu切换到了另一个线程,所以产生了不准确的效果。我们须要确保print self.entries是个逻辑上的原子操纵,以防打印时被其他线程打断。
我们运用了Lock(),来看下边的例子。
from threading import Thread, Lock
import time
lock = Lock()
class CreateListThread(Thread):
def run(self):
self.entries = []
for i in range(10):
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()
此次我们看到了准确的效果。证实了一个线程不能够修正其他线程内部的变量(非全局变量)。