進程線程.實例深刻PYTHON多線程?

單線程演示:python

1.線程就比如<進程線程.形象的說明進程和線程的區別?>中所說的工廠的工人,一個工人幹一個任務叫作單線程
git

2.以下單個線程去訪問4個不一樣的URL,要求返回URL地址和返回碼
github

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
#
# Authors: limanman
# OsChina: http://my.oschina.net/pydevops/
# Purpose:
#
"""
import time
import urllib2


def get_response(url_list):
    """Get url resp code.

    :param url_list: url list
    :return:
    """
    sta_time = time.time()
    for cur_url in url_list:
        print cur_url,
        resp = urllib2.urlopen(cur_url)
        print resp.getcode()
    print 'cost time: %s' % (time.time()-sta_time)

if __name__ == '__main__':
    url_list = [
        'https://www.baidu.com/',
        'https://github.com/',
        'http://www.aliyun.com/',
        'https://www.dnspod.cn/',
    ]
    # 調用執行
    get_response(url_list)
https://www.baidu.com/ 200
https://github.com/ 200
http://www.aliyun.com/ 200
https://www.dnspod.cn/ 200
cost time: 7.49797701836

說明:如上4個URL被順序請求,除非CPU從一個URL得到了迴應,不然不會去請求下一個URL,網絡請求會花費較長時間,因此CPU在等待網絡請求的返回時間內會一直處於閒置狀態安全


多線程演示:網絡

1.線程就比如<進程線程.形象的說明進程和線程的區別?>中所說的工廠的工人,多個工人幹一個任務叫作多線程
多線程

2.以下多個線程去訪問4個不一樣的URL,要求返回URL地址和返回碼app

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
#
# Authors: limanman
# OsChina: http://my.oschina.net/pydevops/
# Purpose:
#
"""
import time
import urllib2
import threading


class MultThread(threading.Thread):
    def __init__(self, url):
        self.url = url
        super(MultThread, self).__init__()

    def run(self):
        print self.url,
        resp = urllib2.urlopen(self.url, data=None, timeout=3)
        print resp.getcode()


def get_response(url_list):
    """Get resp with mulit threads.

    :param url_list: url list
    :return:
    """
    # 建立url_list長度個線程
    sta_time = time.time()
    thread_list = []
    for cur_url in url_list:
        cur_thread = MultThread(cur_url)
        thread_list.append(cur_thread)
        # 通知CPU能夠執行MultThread下面的run方法
        cur_thread.start()
    # 保證全部線程執行完畢後再執行print,不然會先執行print而後再回頭繼續執行線程run方法
    for cur_thread in thread_list:
        cur_thread.join()
    print 'cost time: %s' % (time.time()-sta_time)

if __name__ == '__main__':
    url_list = [
        'https://www.baidu.com/',
        'https://github.com/',
        'http://www.aliyun.com/',
        'https://www.dnspod.cn/',
    ]

    # 調用
    get_response(url_list)
https://www.baidu.com/ https://github.com/ https://www.dnspod.cn/http://www.aliyun.com/ 200
200
200
200
cost time: 1.33794403076

說明:從執行時間上面已經明顯感受有性能上提高,如上是一個多線程減小CPU等待時間,在等待一個線程內的網絡請求返回時,CPU能夠切換到其它線程去進行其它線程內的網絡請求,因此你會發現打印的格式是先把全部的url打印出來,而後等待返回碼,返回一個打印一個,咱們指望一個線程處理一個url,固然也能夠一個線程處理多個url,因此實例化線程類咱們傳入一個url,線程運行意味着執行類裏的run()方法,性能

因此爲每一個url建立一個線程並調用start()方法,告訴CPU能夠執行線程中的run()方法了,咱們但願全部的線程都執行完畢後再計算執行時間,因此咱們對每一個線程調用join()方法,它會通知主線程等待這個線程結束後,纔會執行下一條指令,也就是print那句,須要注意的是CPU可能不會在調用start()後立刻執行run()方法,沒法肯定run()在不一樣線程間的執行順序,對於單獨的一個線程能夠保證順序執行,由於線程內的url會依次被請求並返回結果ui


全局變量線程安全問題:url

1.在<進程線程.形象的說明進程和線程的區別?>中說,車間的空間是工人們共享的,好比許多房間裏是每一個工人均可以進出的,這就象徵一個進程的內存空間是共享的,每一個線程均可以使用這些共享內存

2.<進程線程.形象的說明進程和線程的區別?>中說,每一個房間的大小不一樣,有些房間只能容納一我的,好比廁所,裏面有人的時候其餘人不能進,這表明一個線程使用某些共享內存時,其它線程必須等待它結束,才能使用這一起內存

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
#
# Authors: limanman
# OsChina: http://my.oschina.net/pydevops/
# Purpose:
#
"""
import time
import threading


class MultThread(threading.Thread):
    def __init__(self):
        super(MultThread, self).__init__()

    def run(self):
        global global_num
        currentnum = global_num
        print 'thread - %s currentnum - %s' % (self.name, currentnum)
        global_num = currentnum + 1


if __name__ == '__main__':
    # 全局變量
    global_num = 0

    thread_list = []
    # 開啓50個線程
    for _ in xrange(50):
        cur_thread = MultThread()
        thread_list.append(cur_thread)
        cur_thread.start()
    for cur_thread in thread_list:
        cur_thread.join()

    print
    print 'global_num modify 50 times, should become 50.'
    print 'after 50 times modify, global_num is %s.' % (global_num)
thread - Thread-1 currentnum - 0
thread - Thread-2 currentnum - 1
thread - Thread-3 currentnum - 2
thread - Thread-4 currentnum - 3
thread - Thread-5 currentnum - 4
thread - Thread-6 currentnum - 5
thread - Thread-7 currentnum - 6
thread - Thread-8 currentnum - 7
thread - Thread-9 currentnum - 8thread - Thread-10 currentnum - 8
thread - Thread-11 currentnum - 9
thread - Thread-12 currentnum - 10
thread - Thread-13 currentnum - 11

thread - Thread-14 currentnum - 9
thread - Thread-15 currentnum - 10
thread - Thread-16 currentnum - 11
thread - Thread-17 currentnum - 12
thread - Thread-18 currentnum - 13
thread - Thread-19 currentnum - 14
thread - Thread-20 currentnum - 15
thread - Thread-21 currentnum - 16
thread - Thread-22 currentnum - 17
thread - Thread-23 currentnum - 18
thread - Thread-24 currentnum - 19
thread - Thread-25 currentnum - 20
thread - Thread-26 currentnum - 21thread - Thread-27 currentnum - 21

thread - Thread-28 currentnum - 22
thread - Thread-29 currentnum - 23
thread - Thread-30 currentnum - 24
thread - Thread-31 currentnum - 25
thread - Thread-32 currentnum - 26
thread - Thread-33 currentnum - 27
thread - Thread-34 currentnum - 28
thread - Thread-35 currentnum - 29
thread - Thread-36 currentnum - 30
thread - Thread-37 currentnum - 31
thread - Thread-38 currentnum - 32
thread - Thread-39 currentnum - 33
thread - Thread-40 currentnum - 34
thread - Thread-41 currentnum - 35
thread - Thread-42 currentnum - 36
thread - Thread-43 currentnum - 37
thread - Thread-44 currentnum - 38
thread - Thread-45 currentnum - 39
thread - Thread-46 currentnum - 40
thread - Thread-47 currentnum - 41
thread - Thread-48 currentnum - 42
thread - Thread-49 currentnum - 43
thread - Thread-50 currentnum - 44

global_num modify 50 times, should become 50.
after 50 times modify, global_num is 45.

說明:有一個全局變量,全部的線程都想修改它,全部的線程應該在這個全局變量的基礎上加1,有50個線程,最後這個數值應該變爲50,爲何沒有到50?當global_num是8的時候,Thread-10讀取了global_num,這個時刻cpu將控制權給了另外一個線程Thread-10,Thread-10讀取到的也是8,Thread-9和Thread-10都把global_num加到9,可是咱們指望的是Thread-9和Thread-10兩個線程使global_num+2變爲10,也就是Thread-11爲10,在這裏有了資源競爭,相同的狀況也會發生在其它線程之間,因此出現了最後的結果小於50的狀況


解決資源競爭:

1.在<進程線程.形象的說明進程和線程的區別?>中說一個防止他進入的簡單方法,就是門口加一把鎖,先到的人鎖上門,後到的人看到上鎖,就在門口排隊,等鎖打開再進去,這就叫"互斥鎖",防止多個線程同時讀寫某一塊內存區域

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
#
# Authors: limanman
# OsChina: http://my.oschina.net/pydevops/
# Purpose:
#
"""
import time
import threading


class MultThread(threading.Thread):
    def __init__(self, thread_lock):
        super(MultThread, self).__init__()
        self.thread_lock = thread_lock

    def run(self):
        global global_num
        # 獲取一把鎖
        self.thread_lock.acquire()
        currentnum = global_num
        print 'thread - %s currentnum - %s' % (self.name, currentnum)
        global_num = currentnum + 1
        # 釋放這把鎖
        self.thread_lock.release()

if __name__ == '__main__':
    # 全局變量
    global_num = 0

    # 互斥鎖
    lock = threading.Lock()

    thread_list = []
    # 開啓50個線程
    for _ in xrange(50):
        cur_thread = MultThread(lock)
        thread_list.append(cur_thread)
        cur_thread.start()
    # 等待全部線程結束再執行下面的指令
    for cur_thread in thread_list:
        cur_thread.join()

    print
    print 'global_num modify 50 times, should become 50.'
    print 'after 50 times modify, global_num is %s.' % (global_num)
thread - Thread-1 currentnum - 0
thread - Thread-2 currentnum - 1
thread - Thread-3 currentnum - 2
thread - Thread-4 currentnum - 3
thread - Thread-5 currentnum - 4
thread - Thread-6 currentnum - 5
thread - Thread-7 currentnum - 6
thread - Thread-8 currentnum - 7
thread - Thread-9 currentnum - 8
thread - Thread-10 currentnum - 9
thread - Thread-11 currentnum - 10
thread - Thread-12 currentnum - 11
thread - Thread-13 currentnum - 12
thread - Thread-14 currentnum - 13
thread - Thread-15 currentnum - 14
thread - Thread-16 currentnum - 15
thread - Thread-17 currentnum - 16
thread - Thread-18 currentnum - 17
thread - Thread-19 currentnum - 18
thread - Thread-20 currentnum - 19
thread - Thread-21 currentnum - 20
thread - Thread-22 currentnum - 21
thread - Thread-23 currentnum - 22
thread - Thread-24 currentnum - 23
thread - Thread-25 currentnum - 24
thread - Thread-26 currentnum - 25
thread - Thread-27 currentnum - 26
thread - Thread-28 currentnum - 27
thread - Thread-29 currentnum - 28
thread - Thread-30 currentnum - 29
thread - Thread-31 currentnum - 30
thread - Thread-32 currentnum - 31
thread - Thread-33 currentnum - 32
thread - Thread-34 currentnum - 33
thread - Thread-35 currentnum - 34
thread - Thread-36 currentnum - 35
thread - Thread-37 currentnum - 36
thread - Thread-38 currentnum - 37
thread - Thread-39 currentnum - 38
thread - Thread-40 currentnum - 39
thread - Thread-41 currentnum - 40
thread - Thread-42 currentnum - 41
thread - Thread-43 currentnum - 42
thread - Thread-44 currentnum - 43
thread - Thread-45 currentnum - 44
thread - Thread-46 currentnum - 45
thread - Thread-47 currentnum - 46
thread - Thread-48 currentnum - 47
thread - Thread-49 currentnum - 48
thread - Thread-50 currentnum - 49

global_num modify 50 times, should become 50.
after 50 times modify, global_num is 50.

說明:Lock用來防止競爭條件,達到咱們預想的結果,若是在執行前thread-1得到了鎖,其它線程在t1釋放Lock以前,不會執行相同的操做,咱們想要肯定的是一旦Thread-1已經讀取了global_num,直到完成了修改global_num,其它線程才能夠讀取和修改global_num


線程切換輸出打斷:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
#
# Authors: limanman
# OsChina: http://my.oschina.net/pydevops/
# Purpose:
#
"""
import time
import threading


class MultThread(threading.Thread):
    def __init__(self):
        super(MultThread, self).__init__()

    def run(self):
        self.entries = []
        for i in xrange(10):
            # 強制CPU切換到其它線程
            time.sleep(0.01)
            self.entries.append(i)
        # 打印會亂掉,可是也說明一個問題線程之間變量是相互獨立的
        print self.entries

if __name__ == '__main__':
    thread_list = []
    for _ in xrange(5):
        cur_thread = MultThread()
        thread_list.append(cur_thread)
        cur_thread.start()
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
, 1, 2, 3, 4, 5, 6, 7, 8, 9]

說明:上面用了一個time.sleep()來讓一個線程掛起,強制切換到其它線程,可是並無和咱們指望同樣打印正確結果,當一個線程正在打印時,CPU切換到另外一個線程,因此產生了不正確的結果,咱們須要保證print語句的原子操做,防止打印時被其它線程打斷


保證線程原子操做:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
#
# Authors: limanman
# OsChina: http://my.oschina.net/pydevops/
# Purpose:
#
"""
import time
import threading


class MultThread(threading.Thread):
    def __init__(self, thread_lock):
        self.thread_lock = thread_lock
        super(MultThread, self).__init__()

    def run(self):
        self.entries = []
        for i in xrange(10):
            # 強制CPU切換到其它線程
            time.sleep(0.01)
            self.entries.append(i)
        # 打印會亂掉,可是也說明一個問題線程之間變量是相互獨立的
        self.thread_lock.acquire()
        print self.entries
        self.thread_lock.release()

if __name__ == '__main__':
    # 互斥鎖
    lock = threading.Lock()
    thread_list = []
    for _ in xrange(5):
        cur_thread = MultThread(lock)
        thread_list.append(cur_thread)
        cur_thread.start()
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

說明:此次咱們看到了正確結果,同時也說明了一個問題,一個線程不能夠修改其它線程內部的變量(全局變量除外)


強制線程退出:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
#
# Authors: limanman
# OsChina: http://my.oschina.net/pydevops/
# Purpose:
#
"""
import time
import threading


class MultThread(threading.Thread):
    def __init__(self, thread_lock):
        self.thread_lock = thread_lock
        super(MultThread, self).__init__()

    def run(self):
        self.entries = []
        for i in xrange(10):
            # 強制CPU切換到其它線程
            time.sleep(0.01)
            self.entries.append(i)
        # 打印會亂掉,可是也說明一個問題線程之間變量是相互獨立的
        self.thread_lock.acquire()
        print self.entries
        self.thread_lock.release()

if __name__ == '__main__':
    # 互斥鎖
    lock = threading.Lock()
    thread_list = []
    for _ in xrange(5):
        cur_thread = MultThread(lock)
        thread_list.append(cur_thread)
        # 將主線程設置爲守護線程,主線程結束後,無論子線程是否完成都一併和主線程退出
        cur_thread.setDaemon(True)
        cur_thread.start()

    # 當前線程數若是不等於1,也就是除了守護線程還有其它線程在運行
    while threading.activeCount() != 1:
        # 強制切換到守護線程外的其餘線程運行
        time.sleep(0.01)
    print 'found notice: all thread is finished.'
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
found notice: all thread is finished.

說明: 能夠看出Thread-[1-5]的內容打印都沒有打印出來,cur_thread.setDaemon(True)的操做將父線程設置爲了守護線程,根據setDaemon()方法的含義,守護線程由於不會像join()那樣等待子線程結束後執行下面的語句,因此守護線程會先執行print 'found notice: all thread is finished.'而後就結束了,全部子線程也就結束了,可是爲了實現join()的效果,因此在上面能夠加一個判斷,判斷包含守護線程在內的線程數是否等於1,若是等於1則表示全部的線程已經所有結束.


容許指定線程數更改數據:

1.在<進程線程.形象的說明進程和線程的區別?>說有些房間能夠容納n我的,好比廚房,也就是說,若是人數大於n,多出來的人只能在外面等着,這比如某些內存區域,只能供給固定數目的線程使用

2.<進程線程.形象的說明進程和線程的區別?>說這時的解決辦法,就是在門口掛n把鎖,進去的人就取一把鑰匙,出來的就把鑰匙掛回原處,後來的人發現鑰匙架爲空,就知道必須門前排隊等着,這種作法叫"信號量",用來保證多個線程不會相互衝突

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
#
# Authors: limanman
# OsChina: http://my.oschina.net/pydevops/
# Purpose:
#
"""
import time
import pprint
import threading


class MultThread(threading.Thread):
    def __init__(self, thread_semaphore):
        super(MultThread, self).__init__()
        self.thread_semaphore = thread_semaphore

    def run(self):
        # 獲取鎖
        self.thread_semaphore.acquire()
        # 等待1秒,開始第二波
        print 'run the thread %s' % (self.name)
        time.sleep(2)
        # 釋放鎖
        self.thread_semaphore.release()

if __name__ == '__main__':
    # 建立一個信號量對象,只容許同時5個線程運行,其餘的線程只有其中有線程結束才能運行
    semaphore = threading.BoundedSemaphore(5)
    # 建立100個線程,可是每次只容許5個線程同時容許
    for _ in xrange(10):
        MultThread(semaphore).start()
相關文章
相關標籤/搜索