阅读更多

2顶
0踩

编程语言

转载新闻 3个实例帮你理解Python中的线程

2013-11-26 16:22 by 见习编辑 tuhaihe 评论(0) 有18460人浏览
我们将会看到一些在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()

这次我们看到了正确的结果。证明了一个线程不可以修改其他线程内部的变量(非全局变量)

原文:Understanding Threads in Python / 翻译:acmerfight
2
0
评论 共 0 条 请登录后发表评论

发表评论

您还没有登录,请您登录后再发表评论

相关推荐

  • 用Python的线程来解决生产者消费问题的示例

    我们将使用Python线程来解决Python中的生产者—消费者问题。这个问题完全不像他们在学校中说的那么难。 如果你对生产者—消费者问题有了解,看这篇博客会更有意义。 为什么要关心生产者—消费者问题: 可以帮你更好...

  • python3 多线程实例_3个实例帮你理解Python中的线程

    在本文中,我们将会看到一些在Python中使用线程的实例和如何避免线程之间的竞争。你应当将下边的例子运行多次,以便可以注意到线程是不可预测的和线程每次运行出的不同结果。声明:从这里开始忘掉你听到过的关于GIL...

  • python 彻底理解线程

    Rlock是一个可以被同一个线程请求多次的同步指令.Rlock使用了"拥有的线程"和"递归等级"的概念,处于锁定状态时,Rlock被某个线程拥有.拥有Rlock的线程可以再次调用acquire(), 释放锁时需要调用release()相同次数.可以...

  • python中线程可用于哪些方面_Python中线程的使用

    PythonPython开发Python语言Python中线程的使用 并发:多个任务同一时间段进行并行:多个任务同一时刻进行线程的实现线程模块Python通过两个标准库_thread 和threading,提供对线程的支持 , threading对_thread进行...

  • python销毁线程_python线程销毁

    广告关闭腾讯云11.11云上盛惠 ,精选热门产品助力上云,云服务器首年88元起,买的越多返的越... 扩展《如何正确的终止正在运行的子线程》:https:www.cnblogs.comcreat... 《不要粗暴的销毁python线程》:http:xiaor...

  • python3多线程并发实例_Python 多线程并发脚本示例

    /usr/bin/pythonimport threadingimport sysimport osimport timedef ssh_cmd(ip):#定义一个ssh_cmd函数 用于发呆5秒,输出iptime.sleep(5)print ipdef ssh_cmd_spit(list):#//定义一个ssh_cmd_spit函数,用于执行...

  • python多核多线程编程实例_Python并发编程之多线程

    线程概念线程:在操作系统中,每一个进程有一个地址空间,而且默认就有一个控制线程.线程顾名思义就是一条流水线工作的过程,一条流水线必须数据一个车间,一个车间的工作过程就是一个进程.车间负责把资源整合到一起,是一...

  • python项目中的多线程主要使用在什么地方_Python并发编程之多线程使用

    目录一 开启线程的两种方式```#方式一from threading import Threadimport timedef sayhi(name):time.sleep(2)print('%s say hello' %name)if name == 'main':t=Thread(target=sayhi,args=('egon',))t.start()print...

  • python守护线程_python守护线程

    # 守护线程随着子线程结束而结束,与守护进程不一样--守护进程随着主进程代码执行完毕而结束 # from threadingimport thread # import time # # def func1(): # while true:# time.sleep(1) # prin...

  • python类创建线程_Python并发编程很简单,一文帮你搞清如何创建线程类

    对于Python的并发编程相关的东东,相信通过上次咱们的探讨,大家已经比较清楚了,对于Python创建线程的方式主要有两种,这个上次咱们也已经说过了哦,第一种是使用threading模块的Thread类的构造器来创建线程,这种...

  • python线程回收_python之线程

    只能在一个进程里并发地开启三个线程,如果是单线程,那就只能是,键盘输入时,不能处理文字和自动保存,自动保存时又不能输入和处理文字。之前我们将的socket是不是通过多进程去实现过呀,如果有500个人同时和我聊天...

  • python开十几个线程_36、python并发编程之多线程(操作篇)

    目录:一 threading模块介绍二 开启线程的两种方式三 在一个进程下开启多个线程与在一个进程下开启多个子进程的区别四 练习五 线程相关的其他方法六 守护线程七 Python GIL(Global Interpreter Lock)八 同步锁九 ...

  • python多线程并发100_python并发编程之多线程

    一 threading模块介绍multiprocess模块的完全模仿了threading模块的接口,二者在使用层面,有很大的相似性,因而不再详细介绍二 开启线程的两种方式#方式一from threading import Threadimport timedef sayhi(name):...

  • python多线程 不在main_Python多线程/单线程

    线程:一个程序最小的运行单位。import threading #引入线程模块importtimedefrun():time.sleep(5)print('over')start_time=time.time()run()run()run()run()end_time=time.time()print('run_time', end_time-star.....

  • python多线程多进程协程_Python 中多进程、多线程、协程

    线程:调度执行的最小单位,也叫执行路径,不能独立存在,依赖进程存在一个进程至少有一个线程,叫主线程,而多个线程共享内存(数据共享,共享全局变量),从而极大的提高了程序的运行效率。协程:是一种用户态的轻量...

  • 自学python实例_总结线程进程协程

    1.共同的作用 就是实现多任务,在同一时间,可以同时做多件事情,就比如你在电脑上可以同时上qq,同时听音乐~ ...如果说要真正的多任务,一个cup完成一个任务,两个cup完成两个任务,多核cpu完成...

  • python进程线程协程区别_python 进程、线程与协程的区别

    进程、线程与协程区别总结- 1.进程是计算器最小资源分配单位- 2.线程是CPU调度的最小单位- 3....多进程、多线程根据cpu核数不一样可能是并行的,但是协程是在一个线程中 所以是并发进程:一个运行的...

  • 帮你搞懂Python进程,线程与协程

    在操作系统中,每一个独立运行的程序,都占有 操作系统 分配的资源,这些程序中间互不干涉,都只负责运行自己的程序代码,这就是进程。 但是当操作系统频繁的创建销毁进程时,大量的系统资源被浪费在创建和销毁的...

  • 基于springboot大学生就业信息管理系统源码数据库文档.zip

    基于springboot大学生就业信息管理系统源码数据库文档.zip

  • 基于java的驾校收支管理可视化平台的开题报告.docx

    基于java的驾校收支管理可视化平台的开题报告

Global site tag (gtag.js) - Google Analytics