多線程編程---python

參考 《Python 核心編程》(第三版)

一、Thread模板

缺點:
  • 不支持守護線程:當主線程退出時,所有子線程都將終止,不管它們是否在工作。
  • 同步原語少
#-*- coding: UTF - 8 - *-
import thread
from time import sleep, ctime


def loop0():
    print 'start loop0 at:', ctime()
    sleep(4)
    print 'end loop0 at:', ctime()


def loop1():
    print 'start loop1 at:', ctime()
    sleep(2)
    print 'end loop1 at;', ctime()


def main():
    print 'all start at:', ctime()
    thread.start_new_thread(loop0, ())#派生一個新線程
    thread.start_new_thread(loop1, ())
    sleep(6)
    print 'all end at:', ctime()

if __name__ == '__main__':
    main()


#-*- coding: UTF - 8 - *-
import thread
from time import sleep, ctime

loops = [4, 2]


def loop(nloop, nsec, lock):
    print 'start loop', nloop, 'at:', ctime()
    sleep(nsec)
    print 'end loop', nloop, 'at:', ctime()
    lock.release()  # release():釋放鎖


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

    for i in nloops:
        lock = thread.allocate_lock()  # allocate_lock():分配鎖對象
        lock.acquire()  # acquire():獲得鎖對象
        locks.append(lock)

    for i in nloops:
        thread.start_new_thread(loop, (i, loops[i], locks[i]))
        for i in nloops:
            while locks[i].locked():
                pass
        print 'all end at:', ctime()

if __name__ == '__main__':
    main()




二、threading模板

優(yōu)點:
  • 支持守護線程:如果把一個線程設置為守護線程,就表示這個線程是不重要的,進程退出時不需要等待這個線程執(zhí)行完成
thread.daemon = True

Thread類

方案一:創(chuàng)建Thread實例,傳給它一個函數(shù)
#-*- 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 'end 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()#start():開始執(zhí)行該線程

    for i in nloops:
        threads[i].join()#join(timeout=None):直至啟動的線程終止之前一直掛起。除非給出了timeout(秒),否則一直堵塞

    print 'ending at:', ctime()

if __name__ == '__main__':
    main()
方案二:創(chuàng)建Thread實例,傳給它一個可調(diào)用的類實例
#-*- 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):
        self.func(*self.args)


def loop(nloop, nsec):
    print 'start loop', nloop, 'at:', ctime()
    sleep(nsec)
    print 'end 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()  # start():開始執(zhí)行該線程

    for i in nloops:
        # join(timeout=None):直至啟動的線程終止之前一直掛起。除非給出了timeout(秒),否則一直堵塞
        threads[i].join()

    print 'ending at:', ctime()


if __name__ == '__main__':
    main()

方案三:派生Thread的子類,并創(chuàng)建子類的實例
#-*- coding: UTF - 8 - *-
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):  # run():定義線程功能的方法(通常在子類中被應用開發(fā)者重寫)
        self.func(*self.args)


def loop(nloop, nsec):
    print 'start loop', nloop, 'at:', ctime()
    sleep(nsec)
    print 'end 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()  # start():開始執(zhí)行該線程

    for i in nloops:
        # join(timeout=None):直至啟動的線程終止之前一直掛起。除非給出了timeout(秒),否則一直堵塞
        threads[i].join()

    print 'ending at:', ctime()


if __name__ == '__main__':
    main()
修改上面的MyThread類(把結果保存在實例屬性self.res中,并創(chuàng)建新方法getResult()來獲取其值)>>>方便被導入
class MyThread(threading.Thread):
    def __init__(self, func, args, name=''):
        threading.Thread.__init__(self)
        self.name = name
        self.func = func
        self.args = args

    def getResult(self):
        return self.res

    def run(self):#run():定義線程功能的方法(通常在子類中被應用開發(fā)者重寫)
        self.res=self.func(*self.args)


同步源語:鎖/互斥、信號量

一、鎖:

原理:

當多線程爭奪鎖時,允許第一個獲得鎖的線程進入臨界區(qū),并執(zhí)行代碼。所有之后到達的線程將被堵塞,直到第一個線程執(zhí)行結束,退出臨界區(qū),并釋放鎖。此時其他等待的線程可以獲得鎖并進入臨界區(qū)。(被堵塞的線程是沒有順序的)


應用場景:

特殊的函數(shù)、代碼塊不希望(或不應該)被多個線程同時執(zhí)行
  • 修改數(shù)據(jù)庫
  • 更新文件
  • ......

代碼:

from threading import Lock
lock = Lock()
lock.acquire()#獲取鎖
lock.release()#釋放鎖
#上下文管理器
from __future__ import with_statement
with lock:
    ......#鎖的線程塊
二、信號量:
信號量是最古老的同步原語之一
threading模塊包括兩種信號量類:SemaphoreBoundedSemaphore(BoundedSemaphore額外功能:計數(shù)器永遠不會超過初始值,可以防范其中信號量釋放次數(shù)多于獲得次數(shù)的異常用例)


原理:

它是一個計數(shù)器,當資源消耗(acquire)時,計數(shù)器值減1;當資源釋放(release)時,計數(shù)器值加1


應用場景:

  • 線程擁有有限資源
  • ......

代碼(糖果機):

#-*- coding: UTF - 8 - *-
from atexit import register
from random import randrange
from threading import BoundedSemaphore, Lock, Thread
from time import sleep, ctime

lock = Lock()
Max = 5
candytray = BoundedSemaphore(Max)


def refill():
    lock.acquire()
    print 'Refilling candy...'
    try:
        candytray.release()
    except ValueError:
        print 'full,skipping'
    else:
        print 'OK'
    lock.release()


def buy():
    lock.acquire()
    print 'Buying candy...'
    if candytray.acquire(False):
        print 'OK'
    else:
        print 'empty,skipping'
    lock.release()


def producer(loops):
    for i in xrange(loops):
        refill()
        sleep(randrange(3))


def consumer(loops):
    for i in xrange(loops):
        buy()
        sleep(randrange(3))


def _main():
    print 'starting at:', ctime()
    nloops = randrange(2, 6)
    print 'THE CANDY MACHINE (full with %d bars)!' % Max
    Thread(target=consumer, args=(randrange(nloops, nloops + Max + 2),)).start()
    Thread(target=producer, args=(nloops,)).start()


@register
def _atexit():
    print 'all DONE at:', ctime()


if __name__ == '__main__':
    _main()




生產(chǎn)者-消費者問題(Queue/queue模塊)

原理:

創(chuàng)建一個隊列,生產(chǎn)者(線程)生產(chǎn)時放入商品,消費者(線程)消費時取出商品


應用場景:

生產(chǎn)者-消費者及類似情景【生產(chǎn)時間不確定,消費時間不確定】


代碼:

#-*- coding: UTF - 8 - *-
from random import randint
from time import sleep
from Queue import Queue
from threading3 import MyThread


def writeQ(queue):
    print 'producing object for Q...',
    queue.put('xxx', 1)
    print "size now", queue.qsize()  # qsize():返回隊列大小


def readQ(queue):
    print 'consumed object from Q... size now', queue.qsize()


def writer(queue, loops):
    for i in range(loops):
        writeQ(queue)
        sleep(randint(1, 3))


def reader(queue, loops):
    for i in range(loops):
        readQ(queue)
        sleep(randint(2, 5))


funcs = [writer, reader]
nfuncs = range(len(funcs))


def main():
    nloops = randint(2, 5)
    # Queue(maxsize=0):創(chuàng)建一個先入先出的隊列,如果給出最大值,則在隊列沒有空間時堵塞;否則(沒有指定最大值),為無限隊列。
    q = Queue(32)
    threads = []

    for i in nfuncs:
        t = MyThread(funcs[i], (q, nloops), funcs[i].__name__)
        threads.append(t)

    for i in nfuncs:
        threads[i].start()

    for i in nfuncs:
        threads[i].join()

    print 'all Done'


if __name__ == '__main__':
    main()




concurrent.futures模塊

優(yōu)點:

  • "任務"級別進行操作
  • 不需要過分關注同步和線程/進程的管理

原理:

指定一個給定數(shù)量的線程池/進程池------提交任務------整理結果

代碼:

#-*- coding: UTF - 8 - *-
from concurrent.futures import ThreadPoolExecutor#ThreadPoolExecutor-多線程,ProcessPoolExecutor-多進程
from re import compile
from time import ctime
from urllib.request import urlopen as uopen

REGEX = compile('#([\d,]+) in Books ')
AMZN = 'http://amazon.com/dp/'
ISBNS = {
    '0132269937': 'Core Python Programming',
    '0132356139': 'Python Web Development with Django',
    '0137143419': 'Python Fundamentals',
}


def getRanking(isbn):
    with uopen('{0}{1}'.format(AMZN, isbn)) as page:
        return str(REGEX.findall(page.read())[0],'utf-8')


def _main():
    print ('Start at', ctime(), 'on Amazon...')
    with ThreadPoolExecutor(3) as executor:#ThreadPoolExecutor(n):n代表線程池個數(shù)
        for isbn, ranking in zip(ISBNS, executor.map(getRanking, ISBNS)):
            print ('- %r ranked - %s' % (ISBNS[isbn], ranking))
    print('all Done at:', ctime())


if __name__ == '__main__':
    _main()




實踐

1、Amazon圖書排行排名

#-*- coding: UTF - 8 - *-
from atexit import register#atexit.register()函數(shù):告知腳本結束時間
from re import compile
from threading import Thread
from time import ctime
from urllib2 import urlopen as uopen

REGEX = compile('#([\d,]+) in Books ')
AMZN = 'http://amazon.com/dp/'
ISBNS = {
    '0132269937':'Core Python Programming',
    '0132356139':'Python Web Development with Django',
    '0137143419':'Python Fundamentals',
}

def getRanking(isbn):
    page = uopen('%s%s' % (AMZN,isbn))
    data = page.read()
    page.close()
    return REGEX.findall(data)[0]

def _showRanking(isbn):#函數(shù)名前面的單劃線--->特殊函數(shù)--->只能被本模塊的代碼使用,不能被其他使用本文件作為庫或者工具模塊的應用導入
    print '- %r ranked %s' %(ISBNS[isbn],getRanking(isbn))

def _main():
    print 'At',ctime(),'on Amazon......'
    for isbn in ISBNS:
        #單線程
        # _showRanking(isbn)
        #多線程
        Thread(target=_showRanking,args=(isbn,)).start()

@register
def _atexit():
    print 'all DONE at:',ctime()

if __name__=='__main__':
    _main()
最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,412評論 6 532
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,514評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,373評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,975評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,743評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,199評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,262評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,414評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,951評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,780評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,527評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,218評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,649評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,889評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,673評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,967評論 2 374

推薦閱讀更多精彩內(nèi)容

  • 1 引言 首先區(qū)分幾個概念: 程序——能被計算機執(zhí)行的文件;進程——處于執(zhí)行狀態(tài)的程序,包含計算機為其分配的空間、...
    rebirth_2017閱讀 248評論 0 0
  • Java-Review-Note——4.多線程 標簽: JavaStudy PS:本來是分開三篇的,后來想想還是整...
    coder_pig閱讀 1,665評論 2 17
  • 1.解決信號量丟失和假喚醒 public class MyWaitNotify3{ MonitorObject m...
    Q羅閱讀 890評論 0 1
  • 下面是我自己收集整理的Java線程相關的面試題,可以用它來好好準備面試。 參考文檔:-《Java核心技術 卷一》-...
    阿呆變Geek閱讀 14,884評論 14 507
  • 如果我還年輕 我要像一把鋒利的劍 插在土地上 也要發(fā)出萬丈光芒 如果我還年輕 我會帶著理想 一個永遠不可能實現(xiàn)的理...
    更向遠行閱讀 373評論 0 0