多線程與多進程的優缺點 與 線程的一些方法


多進程:

  優點:可以用多核

  缺點:開銷大

多線程:

  優點:開銷小

  缺點:不能使用多核

  在日常的生活中,我們用到的肯定是多核機器,所以我們只考慮多核的情況,你會說那么根據上面的優缺點,那肯定就用多進程就好了。歐克,那只是你自己的意淫而已,接下來我要解釋一波了,請聽好:

  我們首先確定的點,就是在一個多核的機器上,進行一系列的操作,那么使用多線程好呢?還是多進程好呢?

  在這個時候我要提出一個觀點:就是CPU肯定是用來做計算的,這是毋庸置疑的。

  ok,我們在之前的基礎上,考慮兩種情況:

  1,計算密集的操作:我用代碼來征服你們,看哪一個更好

  

from multiprocessing import Process
import time
def work():
    res = 0
    for i in range(11111100):
        res+=i
if __name__ == '__main__':
    start = time.time()
    l = []
    for i in range(4):
        p = Process(target=work)
        l.append(p)
        p.start()
    for j in l:
        j.join()
    end = time.time()
    print('%s'%(end - start))

運行時間:1.794102430343628 #根據機子的不同可能結果也並不同
多進程
from threading import Thread

import time
def work():
    res = 0
    for i in range(11111100):
        res+=i
if __name__ == '__main__':
    start = time.time()
    l = []
    for i in range(4):
        T = Thread(target=work)
        l.append(T)
        T.start()
    for j in l:
        j.join()
    end = time.time()
    print('%s'%(end - start))

結果:3.125178813934326
多線程

  看結果一目了然,進程很快,你很牛逼,我來說說原理:對於多線程來說,上面已經提到,他的缺點就是無法使用多核,由於gil鎖的存在,他只能一個一個的取搶鎖,所以會慢,多進程則相反

  2,i/o密集的操作:依舊用代碼來征服你:

from threading import Thread

import time


def work():
    time.sleep(2)


if __name__ == '__main__':
    start = time.time()
    l = []
    for i in range(400):
        p = Thread(target=work)
        # p = Process(target=work)
        l.append(p)
        p.start()
    for j in l:
        j.join()
    end = time.time()
    print('%s' % (end - start))

結果:2.048117160797119
多線程
from multiprocessing import Process


import time


def work():
    time.sleep(2)


if __name__ == '__main__':
    start = time.time()
    l = []
    for i in range(400):
        # p = Thread(target=work)
        p = Process(target=work)
        l.append(p)
        p.start()
    for j in l:
        j.join()
    end = time.time()
    print('%s' % (end - start))

結果:from multiprocessing import Process
from threading import Thread

import time


def work():
    time.sleep(2)


if __name__ == '__main__':
    start = time.time()
    l = []
    for i in range(400):
        # p = Thread(target=work)
        p = Process(target=work)
        l.append(p)
        p.start()
    for j in l:
        j.join()
    end = time.time()
    print('%s' % (end - start))

結果:19.68112564086914
多進程

看結果很明顯:我用時間的停留模擬i/o阻塞,進程確實是並發的,但是在i/o阻塞的時候都要等着,無法運行,並且在進程創建的時候開銷大,時間長,即使是並發的,我開了400個進程,機已經多出了那么多的時間,可想而知,開更多會是什么樣。

 

應用:

計算密集 多進程 金融領域

i/o密集 多線程  爬蟲 web socket

死鎖 與遞歸鎖、

所謂死鎖: 是指兩個或兩個以上的進程或線程在執行過程中,因爭奪資源而造成的一種互相等待的現象,若無外力作用,它們都將無法推進下去。此時稱系統處於死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進程稱為死鎖進程,情況如下:

from threading import Thread,Lock
import time
mutexA = Lock()
mutexB = Lock()


class Work(Thread):
    def run(self):
        self.f1()
        self.f2()

    def f1(self):
        mutexA.acquire()
        print('%s 拿到了A鎖 ' % self.name)
        mutexB.acquire()
        print('%s拿到了B鎖' % self.name)
        mutexB.release()
        print('%s 釋放了 B鎖' % self.name)
        mutexA.release()
        print('%s 釋放了 A鎖' % self.name)

    def f2(self):
        mutexB.acquire()
        time.sleep(2)
        print('%s 拿到了B鎖 ' % self.name)
        mutexA.acquire()
        print('%s拿到了A鎖' % self.name)
        mutexA.release()
        print('%s 釋放了 A鎖' % self.name)
        mutexB.release()
        print('%s 釋放了 B鎖' % self.name)

if __name__ == '__main__':
    for i in range(5):
        t = Work()
        t.start()

解決方法:

遞歸鎖:Rlock

Rlock內部有一個count 初始為0 ,鎖一下加1,釋放了就減一。

from threading import Thread,RLock
import time
mutexA = mutexB = RLock()
class Work(Thread):
    def run(self):
        self.f1()
        self.f2()

    def f1(self):
        mutexA.acquire()
        print('%s 拿到了A鎖 ' % self.name)
        mutexB.acquire()
        print('%s拿到了B鎖' % self.name)
        mutexB.release()
        print('%s 釋放了 b鎖' % self.name)
        mutexA.release()
        print('%s 釋放了 a鎖' % self.name)

    def f2(self):
        mutexB.acquire()
        time.sleep(2)
        print('%s 拿到了A鎖 ' % self.name)
        mutexA.acquire()
        print('%s拿到了B鎖' % self.name)
        mutexA.release()
        print('%s 釋放了 b鎖' % self.name)
        mutexB.release()
        print('%s 釋放了 a鎖' % self.name)

if __name__ == '__main__':
    for i in range(5):
        t = Work()
        t.start()
遞歸鎖

信號量Semaphore 他是一種鎖 

Semaphore ()括號內的參數是幾,就說明可以有幾個可以用這段鎖中的代碼,並不是同事哦 誰先運行完,下一個就會進來。

信號量與進程池是有着同一種的用途,但是用法不同,很且差很多

from threading import Thread, Semaphore, currentThread
import time,random
sm = Semaphore(5)


def work():
    sm.acquire()
    print('\033[32m%s 正在上廁所\033[0m ' % currentThread().name)
    time.sleep(random.randint(1, 3))
    print('\033[30%s 走出了廁所\033[0m' % currentThread().name)
    sm.release()
if __name__ == '__main__':
    for i in range(20):
        t = Thread(target=work,)
        t.start()
信號量

Event

線程的一個關鍵特性是每個線程都是獨立運行且狀態不可預測。如果程序中的其 他線程需要通過判斷某個線程的狀態來確定自己下一步的操作,這時線程同步問題就會變得非常棘手。為了解決這些問題,我們需要使用threading庫中的Event對象。 對象包含一個可由線程設置的信號標志,它允許線程等待某些事件的發生。在 初始情況下,Event對象中的信號標志被設置為假。如果有線程等待一個Event對象, 而這個Event對象的標志為假,那么這個線程將會被一直阻塞直至該標志為真。一個線程如果將一個Event對象的信號標志設置為真,它將喚醒所有等待這個Event對象的線程。如果一個線程等待一個已經被設置為真的Event對象,那么它將忽略這個事件, 繼續執行

Event的一些方法:

event = Event()

event.isSet():返回event的狀態值;

event.wait():如果 event.isSet()==False將阻塞線程;

event.set(): 設置event的狀態值為True,所有阻塞池的線程激活進入就緒狀態, 等待操作系統調度;

event.clear():恢復event的狀態值為False。

具體應用在mysql中
from threading import Thread, Event, currentThread
import time

def check_mysql():
    print('%s 正在檢測mysql... ' % currentThread().name)
    time.sleep(4)
    e.set()


def conn_mysql():
    count = 1
    while not e.is_set():
        if count > 4:
            raise ConnectionResetError('鏈接次數過多')
        print('%s 正在等待第%s鏈接mysql' % (currentThread().name, count))
        e.wait(timeout=1)
        count += 1
    print('%s 連接成功' % currentThread().name)
if __name__ == '__main__':
    traffic = Thread(target=check_mysql)
    traffic.start()
    for i in range(3):
        t = Thread(target=conn_mysql)
        t.start()
666

定時器

from threading import Timer
 
 
def hello():
    print("hello, world")
 
t = Timer(1, hello)
t.start()  # after 1 seconds, "hello, world" will be printed
View Code

線程queue

import queue

# q=queue.Queue(3) #先進先出
# q.put('first')
# q.put('second')
# q.put('third')
# # q.put('fourth')
#
# print(q.get())
# print(q.get())
# print(q.get())
隊列q
# q=queue.LifoQueue() #先進后出
# q.put('first')
# q.put('second')
# q.put('third')
# # q.put('fourth')
#
# print(q.get())
# print(q.get())
# print(q.get())
堆棧q
import queue

q=queue.PriorityQueue()
#put進入一個元組,元組的第一個元素是優先級(通常是數字,也可以是非數字之間的比較),數字越小優先級越高
q.put((20,'a'))
q.put((10,'b'))
q.put((30,'c'))

print(q.get())
print(q.get())
print(q.get())
優先級鎖

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM