中文字幕日韩精品一区二区免费_精品一区二区三区国产精品无卡在_国精品无码专区一区二区三区_国产αv三级中文在线

42線程4_Semaphore_GIL

?

專(zhuān)注于為中小企業(yè)提供成都網(wǎng)站建設(shè)、成都網(wǎng)站設(shè)計(jì)服務(wù),電腦端+手機(jī)端+微信端的三站合一,更高效的管理,為中小企業(yè)玉溪免費(fèi)做網(wǎng)站提供優(yōu)質(zhì)的服務(wù)。我們立足成都,凝聚了一批互聯(lián)網(wǎng)行業(yè)人才,有力地推動(dòng)了上千余家企業(yè)的穩(wěn)健成長(zhǎng),幫助中小企業(yè)通過(guò)網(wǎng)站建設(shè)實(shí)現(xiàn)規(guī)模擴(kuò)充和轉(zhuǎn)變。

?

目錄

threading.Semaphore類(lèi):...1

數(shù)據(jù)結(jié)構(gòu)Queue:...5

GIL.5

?

?

?

threading.Semaphore類(lèi):

線程同步技術(shù);

和Lock很像,信號(hào)量?jī)?nèi)部維護(hù)一個(gè)倒計(jì)數(shù)器,每一次acquire都會(huì)減1(release加1),當(dāng)acquire方法發(fā)現(xiàn)計(jì)數(shù)為0就阻塞,直至其它線程對(duì)信號(hào)量release后,計(jì)數(shù)大于0,恢復(fù)阻塞的線程;

?

倒計(jì)數(shù)器永遠(yuǎn)不會(huì)<0,是非負(fù)數(shù),因?yàn)閍cquire時(shí)發(fā)現(xiàn)是0都會(huì)被阻塞;

與linux OS的信號(hào)完全不同;

?

s=threading.Semaphore(value=1),構(gòu)造方法,value<0拋ValueError異常;

s.acquire(blocking=True,timeout=None),獲取信號(hào)量,計(jì)數(shù)器減1,獲取成功返回True;

s.release(),釋放信號(hào)量,計(jì)數(shù)器加1;

?

應(yīng)用場(chǎng)景:

連接池;

因?yàn)橘Y源有限,每開(kāi)啟一個(gè),連接成本很高(TCP三次握手四次揮手等),所以使用連接池;

?

threading.BoundedSemaphore類(lèi):

有界的信號(hào)量,作上界控制;

不允許使用release超出初始值的范圍,否則拋ValueError異常;

?

信號(hào)量和鎖:

鎖,只允許同一個(gè)時(shí)間一個(gè)線程獨(dú)占資源,它是特殊的信號(hào)量,即信號(hào)量計(jì)數(shù)器初值為1;

信號(hào)量,允許多個(gè)線程訪問(wèn)共享資源,但這個(gè)共享資源數(shù)量有限;

鎖,可看作是特殊的信號(hào)量;

?

例:

def work(s:threading.Semaphore):

??? logging.info('in sub')

??? s.acquire()

??? logging.info('end sub')

?

s = threading.Semaphore(3)

?

logging.info(s.acquire())

logging.info(s.acquire())

logging.info(s.acquire())

?

threading.Thread(target=work, args=(s,)).start()

?

print('~'*20)

time.sleep(2)

logging.info(s.acquire(False))

logging.info(s.acquire(timeout=3))

s.release()

print('end main')

輸出:

2018-08-07-10:07:23?????? Thread info: 9360 MainThread True

2018-08-07-10:07:23?????? Thread info: 9360 MainThread True

2018-08-07-10:07:23?????? Thread info: 9360 MainThread True

2018-08-07-10:07:23?????? Thread info: 13180 Thread-1 in sub

~~~~~~~~~~~~~~~~~~~~

2018-08-07-10:07:25?????? Thread info: 9360 MainThread False

2018-08-07-10:07:28?????? Thread info: 9360 MainThread False

2018-08-07-10:07:28?????? Thread info: 13180 Thread-1 end sub

end main

?

例:

s = threading.Semaphore(3)

s.release()?? #直接使用release改變了初始預(yù)設(shè)值

s.release()

s.release()

print(s.__dict__)

輸出:

{'_value': 6, '_cond': <Condition(<unlocked _thread.lock object at 0x00000000012AB5D0>, 0)>}

?

例,解決超出預(yù)設(shè)值:

s = threading.BoundedSemaphore(3)

s.release()

輸出:

Traceback (most recent call last):

? File "E:/git_practice/cmdb/example_threading2.py", line 359, in <module>

??? s.release()

? File "D:\Python\Python35\lib\threading.py", line 480, in release

??? raise ValueError("Semaphore released too many times")

ValueError: Semaphore released too many times

?

例:

連接池應(yīng)有容量(總數(shù)),有一個(gè)工廠方法可獲取連接,能夠把不用的連接返回,供其它調(diào)用者使用;

?

class Conn:

??? def __init__(self, name):

??????? self.name = name

?

class Pool:

??? def __init__(self, count):

??????? self.count = count

??????? # self.pool = []

??????? self.pool = [self._connect('conn-{}'.format(i)) for i in range(count)]

?

??? def _connect(self, conn_name):

??????? return Conn(conn_name)

?

??? def get_conn(self):?? #此方法在多線程時(shí)有安全問(wèn)題;如果池中正好有一個(gè)連接,有可能多個(gè)線程判斷池的長(zhǎng)度是>0的,當(dāng)一個(gè)線程拿走了連接對(duì)象,其它線程再來(lái)pop就拋異常,解決:用Lock或Semaphore

??????? # return self.pool.pop()

???? ???if len(self.pool) > 0:

??????????? return self.pool.pop()

?

??? def return_conn(self, conn:Conn):

??????? self.pool.append(conn)

?

例,連接池改進(jìn):

class Conn:

??? def __init__(self, name):

??????? self.name = name

?

class Pool:

??? def __init__(self, count):

??????? self.count = count

??????? self.pool = [self._connect('conn-{}'.format(i)) for i in range(count)]

??????? # self.sema = threading.Semaphore(count)

??????? self.sema = threading.BoundedSemaphore(count)?? #信號(hào)量比計(jì)算列表長(zhǎng)度要好

?

??? def _connect(self, conn_name):

??????? return Conn(conn_name)

?

??? def get_conn(self):

??????? self.sema.acquire()

??????? data = self.pool.pop()

??????? return data

?

??? def return_conn(self, conn:Conn):

??????? self.pool.append(conn)

??????? self.sema.release()

?

pool = Pool(3)

def work(pool:Pool):

??? conn = pool.get_conn()

??? logging.info(conn)

??? threading.Event().wait(random.randint(1,4))

??? pool.return_conn(conn)

?

for i in range(6):

??? threading.Thread(target=work, args=(pool,), name='worker-{}'.format(i)).start()

輸出:

2018-08-07-14:09:11?????? Thread info: 3264 worker-0 <__main__.Conn object at 0x00000000014E6780>

2018-08-07-14:09:11?????? Thread info: 12452 worker-1 <__main__.Conn object at 0x00000000014E6630>

2018-08-07-14:09:11?????? Thread info: 11468 worker-2 <__main__.Conn object at 0x00000000014E6748>

2018-08-07-14:09:13?????? Thread info: 13228 worker-3 <__main__.Conn object at 0x00000000014E6748>

2018-08-07-14:09:14?????? Thread info: 12692 worker-4 <__main__.Conn object at 0x00000000014E6780>

2018-08-07-14:09:15?????? Thread info: 6748 worker-5 <__main__.Conn object at 0x00000000014E6630>

?

注:

使用信號(hào)量解決資源有限問(wèn)題;

如果池中有資源,請(qǐng)求者獲取資源時(shí)信號(hào)量減1,拿走資源;

當(dāng)請(qǐng)求超過(guò)資源數(shù),請(qǐng)求者只能等待;

當(dāng)使用者用完歸還資源后信號(hào)量加1,等到線程拿到就可喚醒拿走資源;

?

以上,從程序邏輯上分析:

1、如果還沒(méi)有使用信號(hào)量就release,會(huì)怎么樣?

超出預(yù)設(shè)值,解決用threading.BoundedSemaphore;

2、如果使用了信號(hào)量,但還沒(méi)用完?

??????? self.pool.append(conn)

??????? self.sema.release()

如果一種極端情況,計(jì)數(shù)器還差1個(gè)就滿(mǎn)了,有三個(gè)線程A、B、C都執(zhí)行了第一句,都沒(méi)來(lái)得及release,這時(shí)輪到線程A release,然后輪到C release,這時(shí)一定出問(wèn)題,超界了ValueError,因此有界信號(hào)量能保證,一定不能多歸還;

3、很多線程用完了信號(hào)量?

沒(méi)有獲得信號(hào)量的線程都阻塞,沒(méi)有線程和歸還的線程爭(zhēng)搶?zhuān)?dāng)append后才release,這時(shí)才能等待的線程被喚醒,才能pop,即沒(méi)有獲取信號(hào)量就不能pop,這是安全的;

?

?

?

數(shù)據(jù)結(jié)構(gòu)Queue:

標(biāo)準(zhǔn)庫(kù);

提供FIFO和LIFO的Queue,優(yōu)先隊(duì)列;

Queue類(lèi)是線程安全的,適用于多線程間安全的交換數(shù)據(jù),內(nèi)部使用了Lock和Condition;

?

魔術(shù)方法中,說(shuō)實(shí)現(xiàn)容器的大小不準(zhǔn)確?

如果不加鎖,是不可能獲得準(zhǔn)確的大小的,因?yàn)楫?dāng)前線程剛讀取了一個(gè)大小,還沒(méi)取走,就有可能被其它線程改掉了;

?

Queue類(lèi)的size雖加了鎖,但依然不能保證立即get、put就能成功,因?yàn)樽x取大小和get、put方法是分開(kāi)的;

?

?

?

GIL

gobal intepreter lock,全局解釋器鎖;

GIL保證cpyton進(jìn)程中,只有一個(gè)線程執(zhí)行字節(jié)碼,甚至在多核cpu情況下也是如此;ruby中也有GIL;另,其它解釋器沒(méi)有這種情況,如pypy、jython等;

?

cpython中沒(méi)有真正的多線程;

GIL的本質(zhì):理解cpython多線程;

?

cpython中:

IO密集型,由于線程阻塞,就會(huì)調(diào)度其它線程;

cpu密集型,當(dāng)前線程可能會(huì)連續(xù)的獲得GIL,導(dǎo)致其它線程幾乎無(wú)法使用cpu;

?

IO密集型,使用多線程;

cpu密集型,使用多進(jìn)程,繞開(kāi)GIL;

?

新版cpython正努力優(yōu)化GIL問(wèn)題,但不是移除;

如果非要使用多線程且要有效率,請(qǐng)繞行,選擇其它語(yǔ)言go、erlang等;

?

py中絕大多數(shù)內(nèi)置數(shù)據(jù)結(jié)構(gòu)都是原子操作,如lst.append()、lst.pop();

由于GIL的存在,py的內(nèi)置數(shù)據(jù)類(lèi)型在多線程編程時(shí)就變?yōu)榘踩牧?,但?shí)際上它們本身不是線程安全的;

?

項(xiàng)目一開(kāi)始就要固定使用某一解釋器的XX版本;

?

保留GIL的原因:

guido堅(jiān)持的簡(jiǎn)單哲學(xué),對(duì)于初學(xué)者門(mén)檻低,不需要高深的系統(tǒng)知識(shí)也能安全、簡(jiǎn)單的使用py;

若移除GIL,會(huì)降低cpython單線程的執(zhí)行效率;

?

例1:

def calc():

??? sum = 0

??? for _ in range(100000000):

??????? sum += 1

?

start = datetime.datetime.now()

?

calc()

calc()

calc()

calc()

calc()

?

delta = (datetime.datetime.now() - start).total_seconds()

print(delta)

輸出:

32.942885

?

例2:

def calc():

??? sum = 0

??? for _ in range(100000000):

??????? sum += 1

?

start = datetime.datetime.now()

?

lst = []

for _ in range(5):?? #CPU密集型,用多進(jìn)程解決

??? t = threading.Thread(target=calc)

??? t.start()

??? # t.join()?? #t.join()不能放在此處,若放在此處是串行(第1個(gè)線程執(zhí)行完才能循環(huán)執(zhí)行到下一個(gè)線程),不是并行

??? lst.append(t)

for t in lst:

??? t.join()

?

delta = (datetime.datetime.now() - start).total_seconds()

print(delta)

輸出:

32.487859

?

對(duì)比例1和例2:

從結(jié)果上看,單線程和多線程一樣,cpython的多線程沒(méi)有優(yōu)勢(shì)和一個(gè)線程執(zhí)行時(shí)間相當(dāng),因?yàn)镚IL的存在;

?

?

網(wǎng)頁(yè)題目:42線程4_Semaphore_GIL
文章地址:http://www.rwnh.cn/article6/igicog.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供企業(yè)網(wǎng)站制作、自適應(yīng)網(wǎng)站網(wǎng)站設(shè)計(jì)、關(guān)鍵詞優(yōu)化、靜態(tài)網(wǎng)站、外貿(mào)網(wǎng)站建設(shè)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

成都seo排名網(wǎng)站優(yōu)化
泽州县| 恩施市| 晋州市| 溧阳市| 临漳县| 湘潭县| 安多县| 高唐县| 门源| 金川县| 贡山| 盐边县| 潮安县| 石景山区| 永善县| 益阳市| 高雄县| 宜城市| 舞钢市| 鹤壁市| 灵武市| 凤山市| 甘肃省| 墨脱县| 洪江市| 堆龙德庆县| 明水县| 雷州市| 崇义县| 芮城县| 喜德县| 浦东新区| 格尔木市| 景东| 新邵县| 靖远县| 罗源县| 沁源县| 航空| 甘洛县| 土默特左旗|