您的位置:js12345金沙官网登入 > 网络编程 > python进阶学习(四)

python进阶学习(四)

2019-10-02 10:00
import threadfrom time import sleep , ctimedef loop0(): print 'start loop 0 at:', ctime() sleep print 'loop 0 done at:', ctime()def loop1(): print 'start loop 1 at:', ctime() sleep print 'loop 1 done at:', ctime()def main(): print 'starting at:', ctime() thread.start_new_thread ) thread.start_new_thread ) sleep print('all DONE at:', ctimeif __name__ == '__main()__': main()

计算机程序只不过是磁盘中可执行的,二进制(或其它类型)的数据。它们只有在被读取到内存中,被操作系统调用的时候才开始它们的生命期。进程(有时被称为重量级进程)是程序的一次执行。每个进程都有自己的地址空间,内存,数据栈以及其它记录其运行轨迹的辅助数据。操作系统管理在其上运行的所有进程,并为这些进程公平地分配时间。

我们没有写让主线程等待子线程全部完成后再继续的代码,即我们所说的线程需要某种形式的同步,在这个例子中,调用sleep()来作为同步机制。将其设定为6秒是因为我们知道所有线程(用时 4 秒和用时 2 秒的)会在主线程计时到6秒前完成。

什么是进程?

你可能会想到肯定会有比主线程中额外延时 6 秒更好的方法管理线程。由于这个延时使整个代码没有比单线程的版本执行的更快。想这个使用sleep()来进行线程同步是不可靠的。如果循环有独立且执行不同的时间要怎么办呢?我们可能会过早或过晚退出主线程,这就是我们引出锁的原因。

 

这个应用程序中剩下的一个主要区别是增加了sleep调用。为什么这个做呢?因为如果我们没有阻止主线程继续执行,它将会执行下一条语句,显示all DONE at: XXX 然后退出,而loop0()和loop1这两个线程将中止执行。

线程(有时被称为轻量级进程)跟进程有些相似,不同的是,所有的线程运行在同一个进程中,共享相同的运行环境。我们可以想像成是在主进程或“主线程”中并行运行的“迷你进程”。

代码解析:

大部分的工作是在 main() 中完成的,这里使用了3个独立的for循环。首先创建一个锁的列表,通过使用thread.allocate_lock()函数得到锁对象,然后通过acquire()方法取得。取得锁效果相当于‘把锁锁上’。一旦锁被锁上后,就可以把它添加到锁列表lock()中。下一个循环用于派生线程,每个线程会调用loop()函数,并传递循环号、睡眠时间以及用于该线程的锁的这几个参数。那么为什么我们不在上锁的循环中启动线程呢?这里有两个原因:其一,我们想要同步线程,以便“所有的马同时冲出围栏”;其二,获取锁需要花费一点时间,如果线程执行的太快,可能出现获取锁之前线程就执行结束的情况。

在每个线程执行完成时,它会释放自己的锁对象。最后一个循环只是坐在那里等待,直到所有锁都被释放之后才会继续执行。因为我们按照顺序检查每个锁,所有可能会被排在循环列表前面但是执行的较慢的循环拖累。在这种情况下,大部分时间实在等待最前面的线程。当这种线程的锁被释放时,剩下的锁可能早已被释放(也就是说,对应的线程以及执行完毕)。结果就是主线程会飞快地、没有停顿地完成剩下锁的检查。最后,你应该知道我们直接待用这个脚本时,最后几行语句才会执行main()函数。

 

睡眠 4 秒和睡眠 2 秒的代码片段是并发执行的,这样有助于减少整体的运行时间。你甚至可以看到loop1是如何在loop0之前结束的。

 7.2.1、单线程

  在单线程中顺序执行两个循环。一定要一个循环结束后,另一个才能开始。总时间是各个循环运行时间之和。 

onetherad.py

js12345金沙官网登入 1

from time import sleep, ctime 

def loop0():
    print 'start loop 0 at:', ctime()
    sleep(4)
    print 'loop 0 done at:', ctime() 

def loop1():
    print 'start loop 1 at:', ctime()
    sleep(2)
    print 'loop 1 done at:', ctime() 

def main():
    print 'start:', ctime()
    loop0() 
    loop1()
    print 'all end:', ctime() 

if __name__ == '__main__':
    main()

js12345金沙官网登入 2

运行结果:

start loop 0 at: Mon Dec 23 09:59:44 2013
loop 0 done at: Mon Dec 23 09:59:48 2013
start loop 1 at: Mon Dec 23 09:59:48 2013
loop 1 done at: Mon Dec 23 09:59:50 2013
all end: Mon Dec 23 09:59:50 2013

  Python通过两个标准库thread和threading提供对线程的支持。thread提供了低级别的、原始的线程以及一个简单的锁。threading基于Java的线程模型设计。锁(Lock)和条件变量(Condition)在Java中是对象的基本行为(每一个对象都自带了锁和条件变量),而在Python中则是独立的对象。

 

 

start_new_thread()必须包含开始的两个参数,于是即使要执行的函数不需要参数,也需要传递一个空元组。

 7.2.1、threading模块

  我们应该避免使用thread模块,原因是它不支持守护线程。当主线程退出时,所有的子线程不论它们是否还在工作,都会被强行退出。有时我们并不期望这种行为,这时就引入了守护线程的概念。threading模块则支持守护线程。

mtsleep3.py

js12345金沙官网登入 3

#coding=utf-8
import threading 
from time import sleep, ctime 

loops = [4,2] 

def loop(nloop, nsec):
    print 'start loop', nloop, 'at:', ctime() 
    sleep(nsec) 
    print 'loop', nloop, 'done at:', ctime()


def main():
    print 'starting at:', ctime()
    threads = []
    nloops = range(len(loops)) 

    #创建线程
    for i in nloops:
        t = threading.Thread(target=loop,args=(i,loops[i]))
        threads.append(t)

    #开始线程
    for i in nloops:
        threads[i].start()

    #等待所有结束线程
    for i in nloops:
        threads[i].join()


    print 'all end:', ctime() 

if __name__ == '__main__': 
    main()

js12345金沙官网登入 4

运行结果:

js12345金沙官网登入 5

starting at: Mon Dec 23 22:58:55 2013
start loop 0 at: Mon Dec 23 22:58:55 2013
start loop 1 at: Mon Dec 23 22:58:55 2013
loop 1 done at: Mon Dec 23 22:58:57 2013
loop 0 done at: Mon Dec 23 22:58:59 2013
all end: Mon Dec 23 22:58:59 2013

js12345金沙官网登入 6

start()

  开始线程活动

join()

  等待线程终止

 

js12345金沙官网登入,  所有的线程都创建了之后,再一起调用 start()函数启动,而不是创建一个启动一个。而且,不用再管理一堆锁(分配锁,获得锁,释放锁,检查锁的状态等),只要简单地对每个线程调用 join()函数就可以了。

join()会等到线程结束,或者在给了 timeout 参数的时候,等到超时为止。join()的另一个比较重要的方面是它可以完全不用调用。一旦线程启动后,就会一直运行,直到线程的函数结束,退出为止。

 

使用可调用的类

 mtsleep4.py

js12345金沙官网登入 7

#coding=utf-8
import threading 
from time import sleep, ctime 

loops = [4,2] 

class ThreadFunc(object):

    def __init__(self,func,args,name=''):
        self.name=name
        self.func=func
        self.args=args

    def __call__(self):
        apply(self.func,self.args)

def loop(nloop,nsec):
    print "seart loop",nloop,'at:',ctime()
    sleep(nsec)
    print 'loop',nloop,'done at:',ctime()

def main():
    print 'starting at:',ctime()
    threads=[]
    nloops = range(len(loops))

    for i in nloops:
        #调用ThreadFunc实例化的对象,创建所有线程
        t = threading.Thread(
            target=ThreadFunc(loop,(i,loops[i]),loop.__name__)) 
        threads.append(t)

    #开始线程
    for i in nloops:
        threads[i].start()

    #等待所有结束线程
    for i in nloops:
        threads[i].join()

    print 'all end:', ctime() 

if __name__ == '__main__': 
    main()

js12345金沙官网登入 8

 运行结果:

js12345金沙官网登入 9

starting at: Tue Dec 24 16:39:16 2013
seart loop 0 at: Tue Dec 24 16:39:16 2013
seart loop 1 at: Tue Dec 24 16:39:16 2013
loop 1 done at: Tue Dec 24 16:39:18 2013
loop 0 done at: Tue Dec 24 16:39:20 2013
all end: Tue Dec 24 16:39:20 2013

js12345金沙官网登入 10

创建新线程的时候,Thread 对象会调用我们的ThreadFunc 对象,这时会用到一个特殊函数__call__()。由于我们已经有了要用的参数,所以就不用再传到 Thread()的构造函数中。由于我们有一个参数的元组,这时要在代码中使用 apply()函数。

我们传了一个可调用的类(的实例),而不是仅传一个函数。

__init__()

方法在类的一个对象被建立时运行。这个方法可以用来对你的对象做一些初始化。

apply()

apply(func [, args [, kwargs ]]) 函数用于当函数参数已经存在于一个元组或字典中时,间接地调用函数。args是一个包含将要提供给函数的按位置传递的参数的元组。如果省略了args,任何参数都不会被传递,kwargs是一个包含关键字参数的字典。

apply() 用法:

js12345金沙官网登入 11

#不带参数的方法
>>> def say():
    print 'say in'

>>> apply(say)
say in

#函数只带元组的参数
>>> def say(a,b):
    print a,b

>>> apply(say,('hello','虫师'))
hello 虫师

#函数带关键字参数
>>> def say(a=1,b=2):
    print a,b


>>> def haha(**kw):
    apply(say,(),kw)


>>> haha(a='a',b='b')
a b

js12345金沙官网登入 12

 

我们可以看到输出结果与mtsleepA.py 相似。唯一的区别是我们不需要再像mtsleepA.py 那样等待额外的时间后才能结束。通过使用锁,我们可以在所有线程全部完成执行后立即退出。

什么是线程?

本文由js12345金沙官网登入发布于网络编程,转载请注明出处:python进阶学习(四)

关键词: