使用greenlet如何实现Python中的并发?

python开发中,协程(Coroutine也被称为微线程可以在一个函数执行过程中将其挂起,去执行另一个函数,并在必要时将之前的函数唤醒。在Python的语言环境里,协程是相当常用的实现并发的方法。这里我们要介绍一个非常好用的框架greenlet,很多知名的网络并发框架如eventletgevent都是基于它实现的。

  第一个例子

  沿袭我们一直以来的习惯,先从例子开始,这次偷个懒,直接把 官方文档 中的例子拿过来:

  from greenletimport greenlet

  def test1():

  print 12

  gr2.switch()

  print 34

  def test2():

  print 56

  gr1.switch()

  print 78

  gr1 = greenlet(test1)

  gr2 = greenlet(test2)

  gr1.switch()

  这里创建了两个greenlet协程对象,gr1gr2,分别对应于函数test1()test2()。使用greenlet对象的switch()方法,即可以切换协程。上例中,我们先调用”gr1.switch()”,函数test1()被执行,然后打印出”12″;接着由于”gr2.switch()”被调用,协程切换到函数test2(),打印出”56″;之后”gr1.switch()”又被调用,所以又切换到函数test1()。但注意,由于之前test1()已经执行到第5行,也就是”gr2.switch()”,所以切换回来后会继续往下执行,也就是打印”34″;现在函数test1()退出,同时程序退出。由于再没有”gr2.switch()”来切换至函数test2(),所以程序第11”print 78″不会被执行。

  所以,程序运行下来的输出就是:

  很好理解吧。使用switch()方法切换协程,也比”yield”, “next/send”组合要直观的多。上例中,我们也可以看出,greenlet协程的运行,其本质是串行的,所以它不是真正意义上的并发,因此也无法发挥CPU多核的优势,不过,这个可以通过协程+进程组合的方式来解决,本文就不展开了。另外要注意的是,在没有进行显式切换时,部分代码是无法被执行到的,比如上例中的”print 78″

  父子关系

  创建协程对象的方法其实有两个参数”greenlet(run=None, parent=None)”。参数”run”就是其要调用的方法,比如上例中的函数test1()test2();参数”parent”定义了该协程对象的父协程,也就是说,greenlet协程之间是可以有父子关系的。如果不设或设为空,则其父协程就是程序默认的”main”主协程。这个”main”协程不需要用户创建,它所对应的方法就是主程序,而所有用户创建的协程都是其子孙。大家可以把greenlet协程集看作一颗树,树的根节点就是”main”,上例中的”gr1″”gr2″就是其两个字节点。

  在子协程执行完毕后,会自动返回父协程。比如上例中test1()函数退出,代码会返回到主程序。让我们写个更清晰的例子来实验下:

  from greenletimport greenlet

  def test1():

  print 12

  gr2.switch()

  print 34

  def test2():

  print 56

  gr1 = greenlet(test1)

  gr2 = greenlet(test2, gr1)

  gr1.switch()print 78

  这里创建greenlet对象”gr2″时,指定了其父协程是”gr1″。所以在函数test2()里,虽然没有”gr1.switch()”代码,但是在其退出后,程序一样回到了函数test1(),并且执行”print 34″。同样,在test1()退出后,代码回到了主程序,并执行”print 78″。所以,最后的输出就是:

  如果上例中,”gr2″的父协程不是”gr1″而是”main”的话,那test2()运行完毕就会回到主程序并直接打印”78″,这样”print 34″就不会执行。大家可以试一试。

  还有一个重要的点,就是协程退出后,就无法再被执行了。如果上例在函数test1()中,再加一句”gr2.switch()”,运行的结果是一样的。因为第二次调用”gr2.switch()”,什么也不会运行。

  def test1():

  print 12

  gr2.switch()

  print 34

  gr2.switch()

  大家可能会感觉到父子协程之间的关系,就像函数调用一样,一个嵌套一个。的确,其实greenlet协程的实现就是使用了栈,其运行的上下文保存在栈中,”main”主协程处于栈底的位置,而当前运行中的协程就在栈顶。这同函数是一样。此外,在任何时候,你都可以使用”greenlet.getcurrent()”,获取当前运行中的协程对象。比如在函数test2()中执行”greenlet.getcurrent()”,其返回就等于”gr2″

  异常

  既然协程是存放在栈中,那一个协程要抛出异常,就会先抛到其父协程中,如果所有父协程都不捕获此异常,程序才会退出。我们试下,把上面的例子中函数test2()的代码改为:

  def test2():

  print 56

  raise NameError

  程序执行后,我们可以看到Traceback信息:

  File "parent.py", line 14, in

  gr1.switch()

  File "parent.py", line 5, in test1

  gr2.switch()

  File "parent.py", line 10, in test2

  raiseNameError

  同时大家可以试下,如果将”gr2″的父协程设为空,Traceback信息就会变为:

  File "parent.py", line 14, in

  gr1.switch()

  File "parent.py", line 10, in test2

  raiseNameError

  因此,如果”gr2″的父协程是”gr1″的话,异常先回抛到函数test1()的代码”gr2.switch()”处。所以,我们再对函数test1()改动下:

  def test1():

  print 12

  try:

  gr2.switch()

  except NameError:

  print 90

  print 34

  运行后的结果,如果”gr2″的父协程是”gr1″,则异常被捕获,并打印90。否则,异常会被抛出。以上实验很好的证明了,子协程抛出的异常会根据栈里的顺序,依次抛到父协程里。

  有一个异常是特例,不会被抛到父协程中,那就是”greenlet.GreenletExit”,这个异常会让当前协程强制退出。比如,我们将函数test2()改为:

  def test2():

  print 56

  raise greenlet.GreenletExit

  print 78

  那代码行”print 78″永远不会被执行。但这个异常不会往上抛,所以其父协程还是可以正常运行。

  另外,我们可以通过greenlet对象的”throw()”方法,手动往一个协程里抛个异常。比如,我们在test1()里调一个throw()方法:

  def test1():

  print 12

  gr2.throw(NameError)

  try:

  gr2.switch()

  except NameError:

  print 90

  print 34

  这样,异常就会被抛出,运行后的Trackback是这样的:

  File "exception.py", line 21, in

  gr1.switch()

  File "exception.py", line 5, in test1

  gr2.throw(NameError)

  如果将”gr2.throw(NameError)”放在”try”语句中,那该异常就会被捕获,并打印”90″。另外,当”gr2″的父协程不是”gr1″而是”main”时,异常会直接抛到主程序中,此时函数test1()中的”try”语句就不起作用了。

  协程间传递消息

  在介绍生成器时,我们聊过可以使用生成器的send()方法来传递参数。greenlet也同样支持,只要在其switch()方法调用时,传入参数即可。我们再来基于本文第一个例子改造下:

  from greenletimport greenlet

  def test1():

  print 12

  y = gr2.switch(56)

  print y

  def test2(x):

  print x

  gr1.switch(34)

  print 78

  gr1 = greenlet(test1)

  gr2 = greenlet(test2)

  gr1.switch()

  在test1()中调用”gr2.switch()”,由于协程”gr2″之前未被启动,所以传入的参数”56″会被赋在test2()函数的参数”x”上;在test2()中调用”gr1.switch()”,由于协程”gr1″之前已执行到第5”y = gr2.switch(56)”这里,所以传入的参数”34″会作为”gr2.switch(56)”的返回值,赋给变量”y”。这样,两个协程之间的互传消息就实现了。

  让我们将上一篇介绍生成器时写的生产者消费者的例子,改为greenlet实现吧:

  from greenletimport greenlet

  def consumer():

  last = ''

  while True:

  receival = pro.switch(last)

  if receivalis not None:

  print 'Consume %s' % receival

  last = receival

  def producer(n):

  con.switch()

  x = 0

  while x < n:

  x += 1

  print 'Produce %s' % x

  last = con.switch(x)

  pro = greenlet(producer)

  con = greenlet(consumer)

  pro.switch(5)

 

来源:Python-伯乐在线

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 支持向量机非线性回归通用MATLAB程序解析 #### 一、概述 本文将详细介绍一个基于MATLAB的支持向量机(SVM)非线性回归的通用程序。该程序采用支持向量机方法来实现数据的非线性回归,并通过不同的核函数设置来适应不同类型的数据分布。此外,该程序还提供了数据预处理的方法,使得用户能够更加方便地应用此程序解决实际问题。 #### 二、核心功能与原理 ##### 1. 支持向量机(SVM) 支持向量机是一种监督学习模型,主要用于分类和回归分析。对于非线性回归任务,SVM通过引入核技巧(kernel trick)将原始低维空间的非线性问题转换为高维空间的线性问题,从而实现有效的非线性建模。 ##### 2. 核函数 核函数的选择直接影响到模型的性能。本程序内置了三种常用的核函数: - **线性核函数**:`K(x, y) = x'y` - **多项式核函数**:`K(x, y) = (x'y + 1)^d` - **径向基函数(RBF)**:`K(x, y) = exp(-γ|x - y|^2)` 其RBF核函数被广泛应用于非线性问题,因为它可以处理非常复杂的非线性关系。本程序默认使用的是RBF核函数,参数`D`用于控制高斯核函数的宽度。 ##### 3. 数据预处理 虽然程序本身没有直接涉及数据预处理的过程,但在实际应用,对数据进行适当的预处理是非常重要的。常见的预处理步骤包括归一化、缺失值处理等。 ##### 4. 模型参数 - **Epsilon**: ε-insensitive loss function的ε值,控制回归带宽。 - **C**: 松弛变量的惩罚系数,控制模型复杂度与过拟合的风险之间的平衡。 #### 三、程序实现细节 ##### 1. 函数输入与输出 - **输入**: - `X`: 输入特征矩阵,维度为(n, l),其n是特征数量,l是样本数量。 - `Y`: 目标值向量,长度为l。 - `Epsilon`: 回归带宽。 - `C`: 松弛变量的惩罚系数。 - `D`: RBF核函数的参数。 - **输出**: - `Alpha1`: 正的拉格朗日乘子向量。 - `Alpha2`: 负的拉格朗日乘子向量。 - `Alpha`: 拉格朗日乘子向量。 - `Flag`: 标记向量,表示每个样本的类型。 - `B`: 偏置项。 ##### 2. 核心代码解析 程序首先计算所有样本间的核矩阵`K`,然后构建二次规划问题并求解得到拉格朗日乘子向量。根据拉格朗日乘子的值确定支持向量,并计算偏置项`B`。 - **核矩阵计算**:采用RBF核函数,通过`exp(-(sum((xi-xj).^2)/D))`计算任意两个样本之间的相似度。 - **二次规划**:构建目标函数和约束条件,使用`quadprog`函数求解最小化问题。 - **支持向量识别**:根据拉格朗日乘子的大小判断每个样本是否为支持向量,并据此计算偏置项`B`。 #### 四、程序扩展与优化 - **多核函数支持**:可以通过增加更多的核函数选项,提高程序的灵活性。 - **自动调参**:实现参数自动选择的功能,例如通过交叉验证选择最优的`Epsilon`和`C`值。 - **并行计算**:利用MATLAB的并行计算工具箱加速计算过程,特别是当样本量很大时。 #### 五、应用场景 该程序适用于需要进行非线性回归预测的场景,如经济预测、天气预报等领域。通过调整核函数和参数,可以有效应对各种类型的非线性问题。 ### 总结 本程序提供了一个支持向量机非线性回归的完整实现框架,通过灵活的核函数设置和参数调整,能够有效地处理非线性问题。对于需要进行回归预测的应用场景,这是一个非常实用且强大的工具。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值