5.6 函数应用

5.5.1 递归函数设计

递归函数是在内部调用自身的函数。递归函数有两个主要组成部分:

  1. 递归点——以不同参数调用自身

  2. 出口——停止递归调用

例:递归法求双阶乘

def recursion(x):

if x==1:

return 1 #出口1

elif x==2:

return 2 #出口2

else:

return recursion(x-2)*x #递归点

print(recursion(11))

运行结果

1039

例:递归法求Fibonacci数列第10项数值

def fibonacci(x):

if x==0:

return 0 #出口1

elif x==1:

return 1 #出口2

else:

return fibonacci(x-2)+fibonacci(x-1) #递归点

print(fibonacci(10))

运行结果

55

以上两个样例程序的递归点都是是else子句,递归调用的出口都在在if 子句与elif子句。

5.5.2 多线程程序设计

进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。

线程(英语:thread):在引入线程的操作系统中,通常都是把进程作为分配资源的基本单位,而把线程作为独立运行和独立调度的基本单位。一条线程指的是进程中一个单一顺序的控制流,通常在一个进程中可以包含若干个线程,每条线程并行执行不同的任务。当然一个进程中至少有一个线程,不然没有存在的意义。

单线程在程序执行时,所走的程序路径按照连续顺序排下来,前面的必须处理好,后面的才会执行。

在多线程还未出现的时候,想要执行多任务是不可能的,举个简单的例子,如果一位程序员希望听音乐,还希望写程序,那么这两件事情一定得分开执行,要么听完音乐后写程序,要么写完程序后听音乐。可以使用Python语言做简单的抽象:

代码清单

from time import ctime, sleep

def music():

print(“Playing Music at %s” %ctime())

sleep(3)

def coding():

print(“Coding at %s” %ctime())

sleep(3)

if _name_==’main’:

music ()

coding ()

print(“Mission Completely at %s” %ctime())

执行结果

Playing Music at Mon Jun 17 15:14:51 2019

Finish Playing Music at Mon Jun 17 15:14:54 2019

Coding at Mon Jun 17 15:14:54 2019

Finish Coding at Mon Jun 17 15:14:57 2019

Mission Completely at Mon Jun 17 15:14:58 2019

多线程(英语:multithreading):在一个程序中,这些独立运行的程序片段叫作“线程”(Thread),利用它编程的概念就叫作“多线程处理”。多线程是为了同步完成多项任务,不是为了提高运行效率,而是为了提高资源使用效率来提高系统的效率。线程是在同一时间需要完成多项任务的时候实现的。

使用Python内置threading模块,可以实现多线程并行处理。表【】是threading模块下定义的函数与常量.

表 threading模块中的函数与常量(改说法)

函数描述

threading.active_count()

返回当前存活的线程类 Thread 对象。返回的计数等于 enumerate() 返回的列表长度。

threading.current_thread()

返回当前对应调用者的控制线程的 Thread 对象。如果调用者的控制线程不是利用 threading 创建,会返回一个功能受限的虚拟线程对象。

threading.get_ident()

返回当前线程的 “线程标识符”。它是一个非零的整数。它的值没有直接含义,主要是用作 magic cookie,比如作为含有线程相关数据的字典的索引。线程标识符可能会在线程退出,新线程创建时被复用。

threading.enumerate()

以列表形式返回当前所有存活的 Thread 对象。 该列表包含守护线程,current_thread() 创建的虚拟线程对象和主线程。它不包含已终结的线程和尚未开始的线程。

threading.main_thread()

返回主 Thread 对象。一般情况下,主线程是Python解释器开始时创建的线程。

threading.settrace(func)

为所有 threading 模块开始的线程设置追踪函数。在每个线程的 run() 方法被调用前,func 会被传递给 sys.settrace()

threading.setprofile(func)

为所有 threading 模块开始的线程设置性能测试函数。在每个线程的 run() 方法被调用前,func 会被传递给 sys.setprofile()

threading.stack_size([size])

返回创建新线程时使用的线程堆栈大小。可选的size参数指定用于随后创建的线程的堆栈大小,并且必须是0(使用平台或配置的默认值)或至少为32768(32 kib)的正整数值。如果未指定大小,则使用0。

threading.TIMEOUT_MAX

阻塞函数( Lock.acquire(), RLock.acquire(), Condition.wait(), …)中形参 timeout 允许的最大值。传入超过这个值的 timeout 会抛出 OverflowError 异常。

我们使用threading模块中的Thread类对线程线程进行实例化。

代码清单

import threading

threads=[]

t1=threading.Thread(target=music)

t2=threading.Thread(target=coding)

threads.append(t1*2)

threads.append(t2*2)

for t in threads:

t.setDaemon(True)

t.start()

print(“Mission Completely %” %ctime())

执行结果

Playing Music at Mon Jun 17 15:15:43 2019 Coding at Mon Jun 17 15:15:43 2019Mission Completely at Mon Jun 17 15:15:43 2019

在上例中,我们首先创建了一个存放线程的空列表,然后分别创建了music和coding两个线程,并将其存入threads列表中。我们通过for循环来对threads列表中存放的线程进行遍历。值得注意的是,for循环依次对treads中的Thread对象进行遍历,这也就意味着for循环执行的是串行任务,那么这段程序是如何实现并行化操作的呢?这时候,守护进程就开始发挥作用了。

守护进程daemon)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。它不需要用户输入就能运行而且提供某种服务,不是对整个系统就是对某个用户程序提供服务。守护进程一般在系统启动时开始运行,除非强行终止,否则直到系统关机都保持运行。

setDaeman(True)将线程声明为守护线程,必须在Thread.start()方法(启动线程)被调用前进行设置,如果不这么做,那么该线程就会被无线挂起。

我们将【代码清单,替换我】中的代码保存为py文件,通过Python来多次运行,发现输出结果不尽相同。我们希望输出的三个句子的排列顺序完全随机。造成这种现象的原因是,子程序和主程序都是在同一时间启动的,由于主程序先结束,所以,coding函数与music函数中位于sleep之后的print语句不会被执行。如果希望”Mission Completely”字符串所在的语句固定在程序最后执行,我们需要对之前的线程进行阻塞。使用Joint()函数可以在子线程完成运行之前,使该子线程的父线程暂停。

我们在print("Mission Completely at %s \n"%ctime())语句前增添

t.joint()

后再次运行脚本。

执行结果

Playing Music at Mon Jun 17 15:21:44 2019 Coding at Mon Jun 17 15:21:44 2019

Finish Playing Music at Mon Jun 17 15:21:47 2019

Finish Coding at Mon Jun 17 15:21:47 2019

Mission Completely at Mon Jun 17 15:21:47 2019

此时,子线程music()与coding()是同时运行的,且在父线程结束之前完成。

Last updated