線程的回顧

線程python

  • 多任務能夠由多進程完成,也能夠由一個進程內的多線程完成。安全

  • 咱們前面提到了進程是由若干線程組成的,一個進程至少有一個線程多線程

  • 因爲線程是操做系統直接支持的執行單元,所以,高級語言一般都內置多線程的支持,Python也不例外,而且,Python的線程是真正的Posix Thread,而不是模擬出來的線程。函數

  • Python的標準庫提供了兩個模塊:thread和threading,thread是低級模塊,threading是高級模塊,對thread進行了封裝。絕大多數狀況下,咱們只須要使用threading這個高級模塊ui

函數方式 建立線程:操作系統

import threading
import time

def func1():
    i = 0
    while i<5 :
        print('eating......')
        time.sleep(1)
        i+=1

def func2():
    i = 0
    while i<5 :
        print('running......')
        time.sleep(1)
        i+=1

if __name__ == '__main__':
    t1 = threading.Thread(target=func1)
    t2 = threading.Thread(target=func2)
    t1.start() #開始線程
    t2.start()
    t1.join()
    t2.join()
>>>
eating......
running......
eating......
running......
eating......
running......

類的方式:線程

import threading,time
class Mythread(threading.Thread):
    def __init__(self,*args,**kwargs):
        name = kwargs.pop('args')
        self._heroname = name
        super(Mythread, self).__init__(*args,**kwargs)
    def run(self):
        print('currentThread is %s'%threading.currentThread())
        print('hero name %s'%self._heroname)
        print(self.name)

t1 = Mythread(args=('魯班七號'))
t2 = Mythread(args='趙雲')
t3 = Mythread(args='後裔')
t1.start()
t2.start()
t3.start()
>>>
currentThread is <Mythread(Thread-1, started 3924)>
hero name 魯班七號
Thread-1
currentThread is <Mythread(Thread-2, started 11416)>
hero name 趙雲
Thread-2
currentThread is <Mythread(Thread-3, started 5868)>
hero name 後裔
Thread-3

多線程共享全局變量code

多線程中,全部變量都由全部線程共享,因此,任何一個變量均可以被任何一個線程修改,所以,線程之間共享數據最大的危險在於多個線程同時改一個變量,把內容給改亂了
數據混亂:對象

import threading

g_nums = 0

def test1():
    global g_nums
    for x in range(1000000):
        g_nums+=1


def test2():
    global g_nums
    for x in range(1000000):
        g_nums+=1


if __name__ == '__main__':
    t1 = threading.Thread(target=test1)
    t2 = threading.Thread(target=test2)
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print(g_nums)
>>>
1320177

互斥鎖遞歸

當線程同時修改末一個共享數據時,須要進行同步控制。
線程同步可以保證多個線程安全訪問競爭資源,最簡單的同步機制是引入互斥鎖

保證共享數據操做的完整性。每一個對象都對應於一個可稱爲" 互斥鎖" 的標記,這個標記用來保證在任一時刻,只能有一個線程訪問該對象。

import threading
from threading import Lock

lock = Lock()
g_nums = 0

def test1():
    global g_nums
    lock.acquire()
    for x in range(1000000):
        g_nums+=1
    lock.release()


def test2():
    global g_nums
    lock.acquire()
    for x in range(1000000):
        g_nums+=1
    lock.release()


if __name__ == '__main__':
    t1 = threading.Thread(target=test1)
    t2 = threading.Thread(target=test2)
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print(g_nums)
>>>
2000000

RLock(可重入鎖)是一個能夠被同一個線程請求屢次的同步指令。RLock使用了「擁有的線程」和「遞歸等級」的概念,處於鎖定狀態時,RLock被某個線程擁有。擁有RLock的線程能夠再次調用acquire(),釋放鎖時須要調用release()相同次數。

能夠認爲RLock包含一個鎖定池和一個初始值爲0的計數器,每次成功調用 acquire()/release(),計數器將+1/-1,爲0時鎖處於未鎖定狀態。

簡言之:Lock屬於全局,Rlock屬於線程

Lock對比Rlock

#coding:utf-8
 
import threading
lock = threading.Lock() #Lock對象
lock.acquire()
lock.acquire()  #產生了死鎖。
lock.release()
lock.release()
print lock.acquire()
 
 
import threading
rLock = threading.RLock()  #RLock對象
rLock.acquire()
rLock.acquire() #在同一線程內,程序不會堵塞。
rLock.release()
rLock.release()

Condition類

  Condition(條件變量)一般與一個鎖關聯。須要在多個Contidion中共享一個鎖時,能夠傳遞一個Lock/RLock實例給構造方法,不然它將本身生成一個RLock實例。

  能夠認爲,除了Lock帶有的鎖定池外,Condition還包含一個等待池,池中的線程處於等待阻塞狀態,直到另外一個線程調用notify()/notifyAll()通知;獲得通知後線程進入鎖定池等待鎖定。

構造方法:
Condition([lock/rlock])

實例方法:
  acquire([timeout])/release(): 調用關聯的鎖的相應方法。
  wait([timeout]): 調用這個方法將使線程進入Condition的等待池等待通知,並釋放鎖。使用前線程必須已得到鎖定,不然將拋出異常。
 notify(): 調用這個方法將從等待池挑選一個線程並通知,收到通知的線程將自動調用acquire()嘗試得到鎖定(進入鎖定池);其餘線程仍然在等待池中。調用這個方法不會釋放鎖定。使用前線程必須已得到鎖定,不然將拋出異常。
  notifyAll():調用這個方法將通知等待池中全部的線程,這些線程都將進入鎖定池嘗試得到鎖定。調用這個方法不會釋放鎖定。使用前線程必須已得到鎖定,不然將拋出異常。

例子1:生產者消費者:

# encoding: UTF-8
import threading
import time

# 商品
product = None
# 條件變量
con = threading.Condition()


# 生產者方法
def produce():
    global product

    if con.acquire():
        while True:
            if product is None:
                print( 'produce...')
                product = 'anything'

                # 通知消費者,商品已經生產
                con.notify()

            # 等待通知
            con.wait()
            time.sleep(2)


# 消費者方法
def consume():
    global product

    if con.acquire():
        while True:
            if product is not None:
                print( 'consume...')
                product = None

                # 通知生產者,商品已經沒了
                con.notify()

            # 等待通知
            con.wait()
            time.sleep(2)

t1 = threading.Thread(target=produce)
t2 = threading.Thread(target=consume)
t2.start()
t1.start()
>>>
produce...
consume...
produce...
consume...
produce...
consume...
......

例子2:生產者消費者

import threading
import time

condition = threading.Condition()
products = 0

class Producer(threading.Thread):
    def run(self):
        global products
        while True:
            if condition.acquire():
                if products < 10:
                    products += 1
                    print( "Producer(%s):deliver one, now products:%s" %(self.name, products))
                    condition.notify()#不釋放鎖定,所以須要下面一句
                    condition.release()
                else:
                    print( "Producer(%s):already 10, stop deliver, now products:%s" %(self.name, products))
                    condition.wait()#自動釋放鎖定
                time.sleep(2)

class Consumer(threading.Thread):
    def run(self):
        global products
        while True:
            if condition.acquire():
                if products > 1:
                    products -= 1
                    print( "Consumer(%s):consume one, now products:%s" %(self.name, products))
                    condition.notify()
                    condition.release()
                else:
                    print( "Consumer(%s):only 1, stop consume, products:%s" %(self.name, products))
                    condition.wait()
                time.sleep(2)

if __name__ == "__main__":
    for p in range(0, 2):
        p = Producer()
        p.start()

    for c in range(0, 3):
        c = Consumer()
        c.start()
>>>
Producer(Thread-1):deliver one, now products:1
Producer(Thread-2):deliver one, now products:2
Consumer(Thread-3):consume one, now products:1
Consumer(Thread-4):only 1, stop consume, products:1
Consumer(Thread-5):only 1, stop consume, products:1
Producer(Thread-2):deliver one, now products:2
...

Event事件
  Event(事件)是最簡單的線程通訊機制之一:一個線程通知事件,其餘線程等待事件。Event內置了一個初始爲False的標誌,當調用set()時設爲True,調用clear()時重置爲Falsewait()將阻塞線程至等待阻塞狀態。

  Event其實就是一個簡化版的Condition。Event沒有鎖,沒法使線程進入同步阻塞狀態。

構造方法:
Event()

實例方法:
1.isSet(): 當內置標誌爲True時返回True。
2.set(): 將標誌設爲True,並通知全部處於等待阻塞狀態的線程恢復運行狀態。

  1. clear():將標誌設爲False。
  2. wait([timeout]): 若是標誌爲True將當即返回,不然阻塞線程至等待阻塞狀態,等待其餘線程調用set()。
# encoding: UTF-8
import threading
import time

event = threading.Event()


def func():
    # 等待事件,進入等待阻塞狀態
    print( '%s wait for event...' % threading.currentThread().getName())
    event.wait()

    # 收到事件後進入運行狀態
    print( '%s recv event.' % threading.currentThread().getName())


t1 = threading.Thread(target=func)
t2 = threading.Thread(target=func)
t1.start()
t2.start()

time.sleep(2)

# 發送事件通知
print( 'MainThread set event.')
event.set()
>>>
Thread-1 wait for event...
Thread-2 wait for event...
MainThread set event.
Thread-2 recv event.
Thread-1 recv event.

Event對象關鍵的特性是他會喚醒全部的線程。若是咱們編寫的程序只但願喚醒一個單獨的線程,那麼最好使用Semaphore或者Condition對象

#encoding:utf-8
# __author__ = 'donghao'
# __time__ = 2019/4/1 21:30
import threading
import time


def worker(n, seam):
    seam.acquire()
    print('working',n)
    time.sleep(1)


if __name__ == '__main__':
    seam = threading.Semaphore(0)
    nworkers = 10
    for n in range(10):
        t = threading.Thread(target=worker, args=(n, seam,))
        t.start()

    while True:
        time.sleep(1)
        seam.release()
>>>
working 0
working 1
working 2
working 3
working 4
working 5
working 6
......

timer類

Timer(定時器)是Thread的派生類,用於在指定時間後調用一個方法。

構造方法:
Timer(interval, function, args=[], kwargs={})

  • interval: 指定的時間
  • function: 要執行的方法
  • args/kwargs: 方法的參數
# encoding: UTF-8
import threading


def func():
    print( 'hello timer!')


timer = threading.Timer(5, func)
timer.start()

local類
local是一個小寫字母開頭的類,用於管理 thread-local(線程局部的)數據。對於同一個local,線程沒法訪問其餘線程設置的屬性;線程設置的屬性不會被其餘線程設置的同名屬性替換。

能夠把local當作是一個「線程-屬性字典」的字典,local封裝了從自身使用線程做爲 key檢索對應的屬性字典、再使用屬性名做爲key檢索屬性值的細節。

`# encoding: UTF-8
import threading

local = threading.local()
local.tname = '王者榮耀'


def func():
    local.tname = '魯班七號'
    print(local.tname)



t1 = threading.Thread(target=func)
t1.start()
t1.join()

print(local.tname)
>>>
魯班七號
王者榮耀
相關文章
相關標籤/搜索