【4】进程管理-2-多线程操作--threading

进程就是一个应用程序在处理机上的一次执行过程,它是一个动态的概念,而线程是进程中的一部分,进程包含多个线程在运行。threading通过对thread模块进行二次封装,提供了更方便的API来操作线程。这里将介绍如何通过多线程去加速你得Python程序的进程。

一、单线程

例子:

from time import ctime,sleep,time
def music():
    for i in range(2):
        print "I was listening to music. %s" %ctime()
        sleep(1)
def move():
    for i in range(2):
        print "I was at the movies! %s" %ctime()
        sleep(5)

if __name__ == '__main__':
    time_start =time()
    music()
    move()
    print "all over %s" %ctime()
    print time()-time_start

运行结果:

 I was listening to music. Sun Aug 9 12:44:52 2015
 I was listening to music. Sun Aug 9 12:44:53 2015
 I was at the movies! Sun Aug 9 12:44:54 2015
 I was at the movies! Sun Aug 9 12:44:59 2015
 all over Sun Aug 9 12:45:04 2015
 12.0174899101

其实,music()和move()更应该被看作是音乐和视频播放器,至于要播放什么歌曲和视频应该由我们使用时决定。所以,我们对上面代码做了改造:

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

def music(func):
    for i in range(2):
        print "I was listening to %s. %s" %(func,ctime())
        sleep(1)

def move(func):
    for i in range(2):
        print "I was at the %s! %s" %(func,ctime())
        sleep(5)

if __name__ == '__main__':
    time_start=time()
    music(u'爱情买卖')
    move(u'阿凡达')
    print "all over %s" %ctime()
    print time()- time_start

运行结果:

 I was listening to 爱情买卖. Sun Aug 9 12:59:45 2015
 I was listening to 爱情买卖. Sun Aug 9 12:59:46 2015
 I was at the 阿凡达! Sun Aug 9 12:59:47 2015
 I was at the 阿凡达! Sun Aug 9 12:59:52 2015
 all over Sun Aug 9 12:59:57 2015
 12.0150828362

对music()和move()进行了传参处理。体验中国经典歌曲和欧美大片文化。

二、多线程

Thread 是threading模块中最重要的类之一,可以使用它来创建线程。有两种方式来创建线程:

A 创建线程要执行的函数,把这个函数传递进Thread对象里,让它来执行;

B 继承Thread类,创建一个新的class,将要执行的代码 写到run函数里面。

方法一:(创建函数并且传入Thread 对象中:)

#coding=utf-8
import threading
from time import ctime,sleep,time
def music(func):
    for i in range(2):
        print "I was listening to %s. %s" %(func,ctime())
        
        sleep(1)
def move(func):
    for i in range(2):
        print "I was at the %s! %s" %(func,ctime())
        sleep(5)
threads = []
t1 = threading.Thread(target=music,args=(u'爱情买卖',))
threads.append(t1)
t2 = threading.Thread(target=move,args=(u'阿凡达',))
threads.append(t2)
if __name__ == '__main__':
    time_start=time()
    for t in threads:
        t.setDaemon(True)
        t.start()
    print "all over %s" %ctime()
    print time()- time_start

代码解析:

import threading #首先导入threading 模块,这是使用多线程的前提。

threads = []

t1 = threading.Thread(target=music,args=(u’爱情买卖’,))

threads.append(t1)

创建了threads数组,创建线程t1,使用threading.Thread()方法,在这个方法中调用music方法target=music,args方法对music进行传参。 把创建好的线程t1装到threads数组中。 接着以同样的方式创建线程t2,并把t2也装到threads数组。

for t in threads:
    t.setDaemon(True)
    t.start()

最后通过for循环遍历数组。(数组被装载了t1和t2两个线程)

setDaemon()

setDaemon(True)将线程声明为守护线程,必须在start() 方法调用之前设置,如果不设置为守护线程程序会被无限挂起。子线程启动后,父线程也继续执行下去,当父线程执行完最后一条语句print “all over %s” %ctime()后,没有等待子线程,直接就退出了,同时子线程也一同结束。

start() #开始线程活动。

运行结果:

 I was listening to 爱情买卖. Sun Aug 9 13:03:41 2015
 I was at the 阿凡达! Sun Aug 9 13:03:41 2015
 all over Sun Aug 9 13:03:41 2015
 0.000370979309082

从执行结果来看,子线程(muisc 、move )和主线程(print “all over %s” %ctime())都是同一时间启动,但由于主线程执行完结束,所以导致子线程也终止。

进行改进:

if __name__ == '__main__':
    time_start=time()
    for t in threads:
        t.setDaemon(True)
        t.start()
     t.join()
    print "all over %s" %ctime()
    print time()- time_start

我们只对上面的程序加了个join()方法,用于等待线程终止。join()的作用是,在子线程完成运行之前,这个子线程的父线程将一直被阻塞。 注意: join()方法的位置是在for循环外的,也就是说必须等待for循环里的两个进程都结束后,才去执行主进程。如果放到for循环里面去了,这个多线程就没有作用了,不信,可以try。

运行结果:

 I was listening to 爱情买卖. Sun Aug 9 13:08:29 2015
 I was at the 阿凡达! Sun Aug 9 13:08:29 2015
 I was listening to 爱情买卖. Sun Aug 9 13:08:30 2015
 I was at the 阿凡达! Sun Aug 9 13:08:34 2015
 all over Sun Aug 9 13:08:39 2015
 10.0077209473

总耗时为10秒。从单线程时减少了2秒,我们可以把music的sleep()的时间调整为4秒。 单线程的时间增加了,但是双线程的总时间没有变化。

细心的你也会会发现,让第一个程序提早介绍的原因并不是join()没加入,而是t.setDaemon(True),如果第二个程序不加t.setDaemon(True),程序能完整运行下来,那么问题来了,这个setDaemon究竟在里面是什么作用?? 当没有存活的非守护进程时,整个python程序才会退出。

也就是说:如果主线程执行完以后,还有其他非守护线程,主线程是不会退出的,会被无限挂起;必须将线程声明为守护线程之后,如果队列中的数据运行完了,那么整个程序想什么时候退出就退出,不用等待。 setDaemon这个方法基本和join是相反的。当我们在程序运行中,执行一个主线程,如果主线程又创建一个子线程,主线程和子线程就分兵两路,分别运行,那么当主线程完成想退出时,会检验子线程是否完成。如果子线程未完成,则主线程会等待子线程完成后再退出。但是有时候我们需要的是,只要主线程完成了,不管子线程是否完成,都要和主线程一起退出,这时就可以用setDaemon方法啦

例二:

import threading,time
from time import sleep, ctime
def now() :
    return str(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()))
def test(nloop, nsec):
    print 'start loop', nloop, 'at:', now()
    sleep(nsec)
    print 'loop', nloop, 'done at:', now()
def main():
    print 'starting at:', now()
    threadpool=[]
    
    for i in xrange(10):
        
        th = threading.Thread(target=test, args=(i, 2))
        threadpool.append(th)
    for th in threadpool:
        th.start()
    #for th in threadpool:
        #threading.Thread.join(th)
        th.join()
    print 'all Done at:', now()
if __name__ == '__main__':
     main()

运行结果:

tanqianshan@promote:~/python$ python test_thread.py
starting at: 2015-08-09 13:46:29
start loop 0 at: 2015-08-09 13:46:29
 start loopstart loop 2  1 at:at: 2015-08-09 13:46:29
 2015-08-09 13:46:29
 start loop 3 at: 2015-08-09 13:46:29
start loop 4 at: 2015-08-09 13:46:29
start loop 5 at: 2015-08-09 13:46:29
 start loop 6 at: 2015-08-09 13:46:29
 start loop 7start loop 8 at: 2015-08-09 13:46:29
 at:start loop 9 2015-08-09 13:46:29
 at: 2015-08-09 13:46:29
looploop looploop 5looploop 6   20loop  done at:3 2015-08-09 13:46:31
  done at: 2015-08-09 13:46:31
loop8  done at:loop 1 loop 7   2015-08-09 13:46:31
done at: 2015-08-09 13:46:31
done at:9 2015-08-09 13:46:31
done at: 2015-08-09 13:46:31
 4 done at: 2015-08-09 13:46:31
 done at: 2015-08-09 13:46:31
 done at: 2015-08-09 13:46:31
done at: 2015-08-09 13:46:31
all Done at: 2015-08-09 13:46:31

问题:为什么输出的不完整呢?

方法二:

import threading ,time
from time import sleep, ctime
def now() :
    return str( time.strftime( '%Y-%m-%d %H:%M:%S' , time.localtime() ) )
class myThread (threading.Thread) :
    """docstring for myThread"""
    def __init__(self, nloop, nsec) :
        super(myThread, self).__init__()
        self.nloop = nloop
        self.nsec = nsec
    def run(self):
        print 'start loop', self.nloop, 'at:', ctime()
        sleep(self.nsec)
        print 'loop', self.nloop, 'done at:', ctime()
def main():
    thpool=[]
    print 'starting at:',now()
    for i in xrange(10):
        thpool.append(myThread(i,2))
    for th in thpool:
        th.start()
    for th in thpool:
        th.join()
    print 'all Done at:', now()

if __name__ == '__main__':
    main()

运行结果:

tanqianshan@promote:~/python$ python test_thread.py
starting at: 2015-08-09 13:57:51
start loop 0start loop  at: Sun Aug  9 13:57:51 2015
start loop 21  at:at: Sun Aug  9 13:57:51 2015
 Sun Aug  9 13:57:51 2015
start loop 3 at: Sun Aug  9 13:57:51 2015
start loop 4 at:start loop  Sun Aug  9 13:57:51 2015
 start loop 56  at:at:start loop 7  Sun Aug  9 13:57:51 2015
 Sun Aug  9 13:57:51 2015
at: Sun Aug  9 13:57:51 2015
start loop 8 at: Sun Aug  9 13:57:51 2015
 start loop 9 at: Sun Aug  9 13:57:51 2015
loop 0 done at: Sun Aug  9 13:57:53 2015
loop loop 4loop loop 82 loop loop6  7looploop loop 5done at:done at: Sun Aug  9 13:57:53 2015
 3  done at:1  Sun Aug  9 13:57:53 2015
 done at:  Sun Aug  9 13:57:53 2015
 done at: Sun Aug  9 13:57:53 2015
 done at: 9 done at: Sun Aug  9 13:57:53 2015
done at: Sun Aug  9 13:57:53 2015
Sun Aug  9 13:57:53 2015
Sun Aug  9 13:57:53 2015
done at: Sun Aug  9 13:57:53 2015
all Done at: 2015-08-09 13:57:53

讨论:

如果我电脑的cpu只有双核,但是我弄了好几个线程,会出现什么情况?

参考资料:

http://blog.csdn.net/huithe/article/details/5954666

http://www.cnblogs.com/fnng/p/3670789.html

http://www.linuxidc.com/Linux/2014-06/102894.htm

http://blog.chinaunix.net/uid-15117916-id-2777241.html

个人公众号,比较懒,很少更新,可以在上面提问题:

更多精彩,请移步公众号阅读:

Sam avatar
About Sam
专注生物信息 专注转化医学