進(jìn)程和線程
進(jìn)程之間的通信: q = multiprocessing.Queue()
進(jìn)程池之間的通信: q = multiprocessing.Manager().Queue()
線程之間的通信: q = queue.Queue()
功能
- 進(jìn)程裂逐,能夠完成多任務(wù)撩笆,比如 在一臺(tái)電腦上能夠同時(shí)運(yùn)行多個(gè)QQ
- 線程,能夠完成多任務(wù),比如 一個(gè)QQ中的多個(gè)聊天窗口
定義的不同
進(jìn)程是系統(tǒng)進(jìn)行資源分配和調(diào)度的一個(gè)獨(dú)立單位.
線程是進(jìn)程的一個(gè)實(shí)體,是CPU調(diào)度和分派的基本單位,它是比進(jìn)程更小的能獨(dú)立運(yùn)行的基本單位.線程自己基本上不擁有系統(tǒng)資源,只擁有一點(diǎn)在運(yùn)行中必不可少的資源(如程序計(jì)數(shù)器,一組寄存器和棧),但是它可與同屬一個(gè)進(jìn)程的其他的線程共享進(jìn)程所擁有的全部資源
區(qū)別
- 一個(gè)程序至少有一個(gè)進(jìn)程,一個(gè)進(jìn)程至少有一個(gè)線程.
- 線程的劃分尺度小于進(jìn)程(資源比進(jìn)程少)乃正,使得多線程程序的并發(fā)性高。
- 進(jìn)程在執(zhí)行過程中擁有獨(dú)立的內(nèi)存單元,而多個(gè)線程共享內(nèi)存,從而極大地提高了程序的運(yùn)行效率
- 線程不能夠獨(dú)立執(zhí)行护锤,必須依存在進(jìn)程中
優(yōu)缺點(diǎn)
- 線程和進(jìn)程在使用上各有優(yōu)缺點(diǎn):線程執(zhí)行開銷小,但不利于資源的管理和保護(hù)酿傍;而進(jìn)程正相反
互斥鎖
當(dāng)多個(gè)線程幾乎同時(shí)修改某一個(gè)共享數(shù)據(jù)的時(shí)候烙懦,需要進(jìn)行同步控制
線程同步能夠保證多個(gè)線程安全訪問競(jìng)爭(zhēng)資源,最簡(jiǎn)單的同步機(jī)制是引入互斥鎖赤炒。互斥鎖為資源引入一個(gè)狀態(tài):鎖定/非鎖定氯析。
-
某個(gè)線程要更改共享數(shù)據(jù)時(shí),先將其鎖定莺褒,此時(shí)資源的狀態(tài)為“鎖定”掩缓,其他線程不能更改;直到該線程釋放資源癣朗,將資源的狀態(tài)變成“非鎖定”拾因,其他的線程才能再次鎖定該資源旺罢】跤啵互斥鎖保證了每次只有一個(gè)線程進(jìn)行寫入操作,從而保證了多線程情況下數(shù)據(jù)的正確性扁达。
threading模塊中定義了Lock類正卧,可以方便的處理鎖定:
#創(chuàng)建鎖
mutex = threading.Lock()
#鎖定
mutex.acquire([blocking])
#釋放
mutex.release()
多線程-非共享數(shù)據(jù)
對(duì)于多線程中全局變量和局部變量是否共享
- 多線程局部變量
#coding=utf-8
import threading
import time
class MyThread(threading.Thread):
# 重寫 構(gòu)造方法
def __init__(self,num,sleepTime):
threading.Thread.__init__(self)
self.num = num
self.sleepTime = sleepTime
def run(self):
self.num += 1
time.sleep(self.sleepTime)
print('線程(%s),num=%d'%(self.name, self.num))
if __name__ == '__main__':
mutex = threading.Lock()
t1 = MyThread(100,5)
t1.start()
t2 = MyThread(200,1)
t2.start()
運(yùn)行結(jié)果:
線程(Thread-2),num=201
線程(Thread-1),num=101
多線程全局變量
import threading
from time import sleep
def test(sleepTime):
num = 1
sleep(sleepTime)
num+=1
print('---(%s)--num=%d'%(threading.current_thread(), num))
if __name__ == '__main__':
t1 = threading.Thread(target = test,args=(5,))
t2 = threading.Thread(target = test,args=(1,))
t1.start()
t2.start()
運(yùn)行結(jié)果:
---(<Thread(Thread-2, started 10876)>)--num=2
---(<Thread(Thread-1, started 7484)>)--num=2
- 在多線程開發(fā)中,全局變量是多個(gè)線程都共享的數(shù)據(jù)跪解,而局部變量等是各自線程的炉旷,是非共享的
同步應(yīng)用
- 多個(gè)線程有序執(zhí)行
from threading import Thread,Lock
from time import sleep
class Task1(Thread):
def run(self):
while True:
if lock1.acquire():
print("------Task 1 -----")
sleep(0.5)
lock2.release()
class Task2(Thread):
def run(self):
while True:
if lock2.acquire():
print("------Task 2 -----")
sleep(0.5)
lock3.release()
class Task3(Thread):
def run(self):
while True:
if lock3.acquire():
print("------Task 3 -----")
sleep(0.5)
lock1.release()
#使用Lock創(chuàng)建出的鎖默認(rèn)沒有“鎖上”
lock1 = Lock()
#創(chuàng)建另外一把鎖,并且“鎖上”
lock2 = Lock()
lock2.acquire()
#創(chuàng)建另外一把鎖叉讥,并且“鎖上”
lock3 = Lock()
lock3.acquire()
t1 = Task1()
t2 = Task2()
t3 = Task3()
t1.start()
t2.start()
t3.start()
運(yùn)行結(jié)果:
------Task 1 -----
------Task 2 -----
------Task 3 -----
------Task 1 -----
------Task 2 -----
------Task 3 -----
------Task 1 -----
------Task 2 -----
------Task 3 -----
------Task 1 -----
------Task 2 -----
------Task 3 -----
------Task 1 -----
------Task 2 -----
------Task 3 -----
------Task 1 -----
------Task 2 -----
------Task 3 -----
------Task 1 -----
------Task 2 -----
------Task 3 -----
...........`
- 可以使用互斥鎖完成多個(gè)任務(wù)窘行,有序的進(jìn)程工作,這就是線程的同步
生產(chǎn)者與消費(fèi)者模式
Python的Queue模塊中提供了同步的图仓、線程安全的隊(duì)列類罐盔,包括FIFO(先入先出)隊(duì)列Queue,LIFO(后入先出)隊(duì)列LifoQueue救崔,和優(yōu)先級(jí)隊(duì)列PriorityQueue惶看。這些隊(duì)列都實(shí)現(xiàn)了鎖原語(可以理解為原子操作捏顺,即要么不做,要么就做完)纬黎,能夠在多線程中直接使用幅骄。可以使用隊(duì)列來實(shí)現(xiàn)線程間的同步本今。
- 用FIFO隊(duì)列實(shí)現(xiàn)上述生產(chǎn)者與消費(fèi)者問題的代碼如下:
import threading,time
from queue import Queue
class Producer(threading.Thread):
def run(self):
global queue
count = 0
while True:
if queue.qsize() < 1000:
for i in range(100):
count = count +1
msg = '生成產(chǎn)品'+str(count)
queue.put(msg)
print(msg)
time.sleep(0.5)
class Consumer(threading.Thread):
def run(self):
global queue
while True:
if queue.qsize() > 100:
for i in range(3):
msg = self.name + '消費(fèi)了 '+queue.get()
print(msg)
time.sleep(1)
if __name__ == '__main__':
queue = Queue()
for i in range(500):
queue.put('初始產(chǎn)品'+str(i))
for i in range(2):
p = Producer()
p.start()
for i in range(5):
c = Consumer()
c.start()
運(yùn)行結(jié)果:
生成產(chǎn)品1
生成產(chǎn)品2
生成產(chǎn)品1
生成產(chǎn)品3
生成產(chǎn)品2
生成產(chǎn)品4
Thread-3消費(fèi)了 初始產(chǎn)品0
生成產(chǎn)品3
生成產(chǎn)品5
Thread-3消費(fèi)了 初始產(chǎn)品1
生成產(chǎn)品4
生成產(chǎn)品6
Thread-4消費(fèi)了 初始產(chǎn)品2
Thread-3消費(fèi)了 初始產(chǎn)品3
生成產(chǎn)品5
生成產(chǎn)品7
Thread-4消費(fèi)了 初始產(chǎn)品4
生成產(chǎn)品6
生成產(chǎn)品8
Thread-5消費(fèi)了 初始產(chǎn)品5
Thread-4消費(fèi)了 初始產(chǎn)品6
...........
- 此時(shí)就出現(xiàn)生產(chǎn)者與消費(fèi)者的問題
Queue的說明
1.對(duì)于Queue拆座,在多線程通信之間扮演重要的角色
2.添加數(shù)據(jù)到隊(duì)列中,使用put()方法
3.從隊(duì)列中取數(shù)據(jù)冠息,使用get()方法
4.判斷隊(duì)列中是否還有數(shù)據(jù)懂拾,使用qsize()方法