python多线程入门之旅一

所有代码来自python核心编程

参考python核心编程一书,学习多线程工作模式,多线程实现主要模块thread,threading,Queue等。

首先实现单线程一段代码:

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 'starting at:', ctime()
    loop0()
    loop1()
    print 'all done at:', ctime()

if __name__ == '__main__':
    main()

运行结果如下:能看到两个函数按顺序在6秒内完成

starting at: Thu Jun 08 16:42:00 2017
start loop 0 at: Thu Jun 08 16:42:00 2017
loop 0 done at: Thu Jun 08 16:42:04 2017
start loop 1 at: Thu Jun 08 16:42:04 2017
loop 1 done at: Thu Jun 08 16:42:06 2017
all done at: Thu Jun 08 16:42:06 2017

我们将首先利用thread修改代码,来看看效果

import thread
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 'starting at:', ctime()
    a=thread.start_new_thread(loop0,())
    b=thread.start_new_thread(loop1,())
    sleep(6)
    print 'all done at:', ctime()

if __name__ == '__main__':
    main()

运行结果如下:我们能看到loop0和loop1是并发执行的

starting at: Thu Jun 08 16:44:44 2017
start loop 1 at: Thu Jun 08 16:44:44 2017
start loop 0 at: Thu Jun 08 16:44:44 2017
loop 1 done at: Thu Jun 08 16:44:46 2017
loop 0 done at: Thu Jun 08 16:44:48 2017
all done at: Thu Jun 08 16:44:50 2017

我们添加了sleep(6)来实现子线程与主线程的同步,我们可以尝试把sleep(6)改为sleep(2)来看看效果:

starting at: Thu Jun 08 16:47:52 2017
start loop 0 at: Thu Jun 08 16:47:52 2017
start loop 1 at: Thu Jun 08 16:47:52 2017
all done at: Thu Jun 08 16:47:54 2017
Unhandled exception in thread started by 
sys.excepthook is missing
lost sys.stderr

直接导致子线程未结束时候主线程已经退出
下面的例子我们再利用thread的锁机制来保证线程的同步

import thread
from time import sleep,ctime

loops = [4,2]

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

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

    for i in nloops:
        lock = thread.allocate_lock()
        lock.acquire()
        locks.append(lock)

    for i in nloops:
        thread.start_new_thread(loop,(i,loops[i],locks[i]))
        #sleep(1)

    for i in nloops:
        while locks[i].locked():
            pass

    print 'all done at:', ctime()

if __name__ == '__main__':
    main()

运行效果:

starting at: Thu Jun 08 17:05:59 2017
start loop 0 at: Thu Jun 08 17:05:59 2017start loop
 1 at: Thu Jun 08 17:05:59 2017
loop 1 done at: Thu Jun 08 17:06:01 2017
loop 0 done at: Thu Jun 08 17:06:03 2017
all done at: Thu Jun 08 17:06:03 2017

我们能看到两个线程是并发的,并且主线程等子线程结束后才结束的。但是与书上不一致的是启动线程中的输出是乱掉了,不清楚为何和书上为何不一致。
根据作者的解释建议我们尽量不要使用thread模块,而使用更高级的threading模块。
下面我们先看下threading模块的对象:

threading模块对象

利用threading模块的thread类有三种方法:

• 创建Thread 的实例,传给它一个函数。
• 创建Thread 的实例,传给它一个可调用的类实例。
• 派生Thread 的子类,并创建子类的实例。

首先利用第一种方法来修改上面的例子:

• 创建Thread 的实例,传给它一个函数。

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, '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 done at:', ctime()


if __name__ == '__main__':
    main()

运行效果:跟之前的代码对比,我们用一组thread对象代替了之前实现的锁,并且代码不会立即执行,只在你希望它执行的时候执行;另外使用join()方法比等待锁释放的无限循环更清晰。

starting at: Fri Jun 09 10:21:45 2017
start loop 0 at: Fri Jun 09 10:21:45 2017
start loop 1 at: Fri Jun 09 10:21:45 2017
loop 1 at: Fri Jun 09 10:21:47 2017
loop 0 at: Fri Jun 09 10:21:49 2017
all done at: Fri Jun 09 10:21:49 2017

接着我们使用第二种方法来修改代码

• 创建Thread 的实例,传给它一个可调用的类实例。

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):
        self.func(*self.args)

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

def main():
    print 'starting at:', ctime()
    threads = []
    nloops = range(len(loops))
    for i in nloops:
        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 done at:', ctime()


if __name__ == '__main__':
    main()

运行结果:在上个代码中添加一个新类ThreadFunc即得到这个代码,比起一个函数有更好的灵活性,而不仅仅是单个函数

starting at: Fri Jun 09 10:31:34 2017
start loop 0 at: Fri Jun 09 10:31:34 2017
start loop 1 at: Fri Jun 09 10:31:34 2017
loop 1 at: Fri Jun 09 10:31:36 2017
loop 0 at: Fri Jun 09 10:31:38 2017
all done at: Fri Jun 09 10:31:38 2017

我们再看看第三种方式的代码:

• 派生Thread 的子类,并创建子类的实例。

import threading
from time import sleep, ctime

loops = [4, 2]


class MyThread(threading.Thread):
    def __init__(self, func, args, name=''):
        threading.Thread.__init__(self)
        self.name = name
        self.func = func
        self.args = args

    def run(self):
        self.func(*self.args)

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

def main():
    print 'starting at:', ctime()
    threads = []
    nloops = range(len(loops))
    for i in nloops:
        t = MyThread(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 done at:', ctime()


if __name__ == '__main__':
    main()

本例直接对Thread子类化,使我们具有更多的灵活性。
使用这种模式必须首先先调用基类的构造函数,即

基类构造函数调用

还有之前的call()特殊方法必须写为run()。
下面是运行结果:

starting at: Fri Jun 09 12:10:54 2017
start loop 0 at: Fri Jun 09 12:10:54 2017
start loop 1 at: Fri Jun 09 12:10:54 2017
loop 1 at: Fri Jun 09 12:10:56 2017
loop 0 at: Fri Jun 09 12:10:58 2017
all done at: Fri Jun 09 12:10:58 2017

如果我们要获取子线程返回的结果怎么处理呢,下篇文章我们在讨论。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,511评论 1 330
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,495评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,595评论 0 225
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,558评论 0 190
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,715评论 3 270
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,672评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,112评论 2 291
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,837评论 0 181
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,417评论 0 228
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,928评论 2 232
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,316评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,773评论 2 234
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,253评论 3 220
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,827评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,440评论 0 180
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,523评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,583评论 2 249

推荐阅读更多精彩内容