python基礎---->進程、線程及相關等


基本概念

進程

一、什麼是進程?

  程序並不能單獨運行,只有將程序裝載到內存中,系統爲它分配資源才能運行,而這種執行的程序就稱之爲進程。python

  程序和進程的區別就在於:程序是指令的集合,它是進程運行的靜態描述文本;進程是程序的一次執行活動,屬於動態概念。編程

  簡單點說就是:進程就是一個程序在一個數據集上的一次動態執行過程。安全

二、進程的特色:

  一個時間點只能作一件事,不能同時幹兩件及以上的事;數據結構

  在執行的過程進程若是阻塞,例如等待輸入,整個進程就會掛起,即便進程中有些工做不依賴於輸入的數據,也將沒法執行。多線程

線程

一、什麼是線程(Thread)?

  線程也叫輕量級進程,它是一個基本的CPU執行單元,也是程序執行過程當中的最小單元併發

  一條線程指的是進程中一個單一順序的控制流,一個進程中能夠併發多個線程,每條線程並行執行不一樣的任務;app

二、線程的特色:

  突破一個進程只能幹同樣事的缺陷,使到進程內併發成爲可能;async

  線程的引入減少了程序併發執行時的開銷,提升了操做系統的併發性能; ide

進程和線程的區別 

  線程是執行的指令集 , 進程是資源的集合函數

  線程的啓動速度要比進程的啓動速度要快

  兩個線程的執行速度是同樣的,進程與線程的運行速度是沒有可比性的

  線程共享建立它的進程的內存空間 , 進程的內存是獨立的

  兩個線程共享的數據都是同一份數據 , 兩個子進程的數據不是共享的 , 並且數據是獨立的

  同一個進程的線程之間能夠直接交流 , 同一個主進程的多個子進程之間是不能夠進行交流 , 若是兩個進程之間須要通訊 , 就必需要經過一箇中間代理來實現

  一個新的線程很容易被建立 , 一個新的進程建立須要對父進程進行一次克隆

  一個線程能夠控制和操做同一個進程裏的其餘線程 , 線程與線程之間沒有隸屬關係 , 可是進程只能操做子進程

  改變主線程 , 有可能會影響到其餘線程的行爲 , 可是對於父進程的修改是不會影響子進程

python threading模塊

線程有兩種調用方式:

直接調用:

import threading
import time

def run(n):         #定義線程要運行的函數
    print('task',n)
    time.sleep(2)

if __name__ == '__main__':
    t1 = threading.Thread(target=run,args=(1,))          #生成一個線程
    t2 = threading.Thread(target=run,args=(2,))          #生成一個另外線程
    
    t1.start()    # 啓動線程
    t2.start()    # 啓動另一個線程

    print(t1.getName())    # 獲取線程名
    print(t2.getName())

print('I am main thread')                #主線程

#這個進程裏面有三個線程,1個主線程,t1,t2兩個子線程
#子線程和主線程是同步開啓的,主線程結束後,要等子線程所有結束後,進程纔會關閉

PS:線程相關的方法:

start        線程準備就緒,等待CPU調度
setName      爲線程設置名稱
getName      獲取線程名稱
run          線程被cpu調度後自動執行線程對象的run方法

# 下面會講到
setDaemon    將線程聲明爲守護線程,必須在start() 方法調用以前設setDaemon(),只要主線程完成了,無論子線程是否完成,都要和主線程一塊兒退出
join         逐個執行每一個線程,執行完畢後繼續往下執行,該方法保證當前線程執行完成後再執行其它線程,使得多線程變得無心義。

繼承式調用:

import threading
import time

class MyThread(threading.Thread):   #繼承threading,Thread模塊
    def __init__(self,n):
        threading.Thread.__init__()    #繼承父類
        # 或者super(MyThread, self).__init__() 

        self.n = n

    def run(self):                 #定義每一個線程要運行的函數,必須用run
        print('task',self.n)
        time.sleep(2)


if  __name__ == '__main__':
    t1 = MyThread(1)
    t2 = MyThread(2)

    t1.start()
    t2.start()

print('I am main thread')

jion和setDaemon:

主線程:當一個程序啓動時 , 就有一個進程被操做系統建立 , 與此同時一個線程也馬上運行 , 該線程一般叫作程序的主線程;

子線程 : 由於程序是開始時就執行的 , 若是你須要再建立線程 , 那麼建立的線程就是這個主線程的子線程;

join應用:

import threading
import time

def run(n):
    print("task ",n )
    time.sleep(2)

start_time = time.time()
t_objs = []    # 存線程實例

for i in range(10):        #生成10個線程
    t = threading.Thread(target=run,args=("t-%s" %i ,))
    t.start()
    t_objs.append(t)  #爲了避免阻塞後面線程的啓動,不在這裏join,先放到一個列表裏

for t in t_objs:      #循環線程實例列表,等待全部線程執行完畢
    t.join()

print("---all threads has finished...")
print("cost:",time.time() - start_time)

setDaemon應用:

import threading
import time

def run(n):
    print('task',n)
    time.sleep(2)
    print('i am 子線程')     #主線程結束,setDaemon無論有沒有運行完都會被銷燬

if __name__ == '__main__':
    t1 = threading.Thread(target=run,args=(1,))
    t2 = threading.Thread(target=run,args=(2,))
    t1.setDaemon(True)   #設置守護線程,放在start以前
    t1.start()
    t2.setDaemon(True)
    t2.start()

print('I am main thread')

結果:
task 1
task 2
I am main thread

線程鎖

lock:若是有多個進程對同一文件進行修改 , 就會形成錯亂 , 因此咱們爲了保護文件數據的安全 , 就須要給其進行加鎖,join爲總體串行 , lock爲局部串行

Rlock(遞歸鎖):在線程間共享多個資源的時候,若是兩個線程分別佔有一部分資源而且同時等待對方的資源,就會形成死鎖,由於系統判斷這部分資源都正在使用,全部這兩個線程在無外力做用下將一直等待下去。就是解決死鎖的問題

加鎖:

import time
import threading

def addNum():
    global num #在每一個線程中都獲取這個全局變量
    print('--get num:',num )
    time.sleep(1)
    lock.acquire() #修改數據前加鎖
    num  -=1 #對此公共變量進行-1操做
    lock.release() #修改後釋放

num = 100  #設定一個共享變量
thread_list = []
lock = threading.Lock() #生成全局鎖
for i in range(100):
    t = threading.Thread(target=addNum)
    t.start()
    thread_list.append(t)

for t in thread_list: #等待全部線程執行完畢
    t.join()

print('final num:', num )

Semaphore(信號量)

互斥鎖 同時只容許一個線程更改數據,而Semaphore是同時容許必定數量的線程更改數據 ,好比食堂打飯有5個窗口,那最多隻容許5我的同時打飯,後面的人只能等裏面有人打完才能去打。

import threading, time

def run(n):
    semaphore.acquire()
    time.sleep(1)
    print("run the thread: %s\n" % n)
    semaphore.release()

if __name__ == '__main__':
    semaphore = threading.BoundedSemaphore(5)  # 最多容許5個線程同時運行
    for i in range(20):
        t = threading.Thread(target=run, args=(i,))
        t.start()
while threading.active_count() != 1:
    pass
else:
    print('----all threads done---')
semaphore

Queue(隊列)

Queue是python標準庫中的線程安全的隊列(FIFO)實現,提供了一個適用於多線程編程的先進先出的數據結構,即隊列,用來在生產者和消費者線程之間的信息傳遞

基本FIFO隊列

FIFO即First in First Out,先進先出。Queue提供了一個基本的FIFO容器,使用方法很簡單,maxsize是個整數,指明瞭隊列中能存放的數據個數的上限。一旦達到上限,插入會致使阻塞,直到隊列中的數據被消費掉。若是maxsize小於或者等於0,隊列大小沒有限制。

簡單的FIFO:

import Queue

q = Queue.Queue()

for i in range(5):
    q.put(i)

while not q.empty():
    print q.get()
0
1
2
3
4
結果

LIFO隊列

LIFO即Last in First Out,後進先出。與棧的相似;

簡單的LIFO:

import Queue

q = Queue.LifoQueue()

for i in range(5):
    q.put(i)

while not q.empty():
    print q.get()
4
3
2
1
0
結果

優先級隊列

import Queue
import threading

class Job(object):
    def __init__(self, priority, description):
        self.priority = priority
        self.description = description
        print('Job:',description)
        return
    def __cmp__(self, other):
        return cmp(self.priority, other.priority)

q = Queue.PriorityQueue()

q.put(Job(3, 'level 3 job'))
q.put(Job(10, 'level 10 job'))
q.put(Job(1, 'level 1 job'))

def process_job(q):
    while True:
        next_job = q.get()
        print('for:', next_job.description)
        q.task_done()

workers = [threading.Thread(target=process_job, args=(q,)),
        threading.Thread(target=process_job, args=(q,))
        ]

for w in workers:
    w.setDaemon(True)
    w.start()

q.join()
Job: level 3 job
Job: level 10 job
Job: level 1 job
for: level 1 job
for: level 3 job
for: job: level 10 job
結果

經常使用方法

qsize()          
#看隊列大小

task_done()
# 意味着以前入隊的一個任務已經完成。由隊列的消費者線程調用。
# 每個get()調用獲得一個任務,接下來的task_done()調用告訴隊列該任務已經處理完畢。若是當前一個join()正在阻塞,它將在隊列中的全部任務都處理完時恢復執行(即每個由put()調用入隊的任務都有一個對應的task_done()調用)。
join() # 阻塞調用線程,直到隊列中的全部任務被處理掉。只要有數據被加入隊列,未完成的任務數就會增長。當消費者線程調用task_done()(意味着有消費者取得任務並完成任務),未完成的任務數就會減小。當未完成的任務數降到0,join()解除阻塞。 put(item[, block[, timeout]]) # 將item放入隊列中。 # 一、若是可選的參數block爲True且timeout爲空對象(默認的狀況,阻塞調用,無超時)。 # 二、若是timeout是個正整數,阻塞調用進程最多timeout秒,若是一直無空空間可用,拋出Full異常(帶超時的阻塞調用)。 # 二、若是block爲False,若是有空閒空間可用將數據放入隊列,不然當即拋出Full異常其非阻塞版本爲put_nowait等同於put(item, False) get([block[, timeout]]) # 從隊列中移除並返回一個數據。block跟timeout參數同put方法 # 其非阻塞方法爲`get_nowait()`至關與get(False) full() #判斷隊列是否有數據 返回bool值 empty() # 若是隊列爲空,返回True,反之返回False

Event事件

python線程的事件用於主線程控制其餘線程的執行,事件主要提供了三個方法 set、wait、clear。

事件處理的機制:全局定義了一個「Flag」,若是「Flag」值爲 False,那麼當程序執行 event.wait 方法時就會阻塞,若是「Flag」值爲True,那麼event.wait 方法時便再也不阻塞。

# -*- coding:utf-8 -*-
# @Author  : Clint
import threading


def do(event):
    print('start')
    
    event.wait()
    print('execute')


event_obj = threading.Event()
for i in range(10):
    t = threading.Thread(target=do, args=(event_obj,))
    t.start()

event_obj.clear()
inp = input('input:')
if inp == 'true':
    event_obj.set()

Condition(條件)

使得線程等待,只有知足某條件時,才釋放n個線程

def condition_func():

    ret = False
    inp = input('>>>')
    if inp == '1':
        ret = True

    return ret


def run(n):
    con.acquire()
    con.wait_for(condition_func)
    print("run the thread: %s" %n)
    con.release()

if __name__ == '__main__':

    con = threading.Condition()
    for i in range(10):
        t = threading.Thread(target=run, args=(i,))
        t.start()

Timer(定時器)

from threading import Timer
 
 
def hello():
    print("hello, world")
 
t = Timer(1, hello)
t.start()  # 一秒後打印

進程池

進程池內部維護一個進程序列,當使用時,則去進程池中獲取一個進程,若是進程池序列中沒有可供使用的進進程,那麼程序就會等待,直到進程池中有可用進程爲止。

進程池中有兩個方法:apply、apply_async

# -*- coding:utf-8 -*-
# @Author  : Clint
from multiprocessing import Process, Pool
import time


def Foo(i):
    time.sleep(2)
    return i + 100


def Bar(arg):
    print(arg)


pool = Pool(5)
# print pool.apply(Foo,(1,))
# print pool.apply_async(func =Foo, args=(1,)).get()

for i in range(10):
    pool.apply_async(func=Foo, args=(i,), callback=Bar)

print('end')

pool.close()
pool.join()   # 進程池中進程執行完畢後再關閉,若是註釋,那麼程序直接關閉。
相關文章
相關標籤/搜索