成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

「Python 面試」第三次更新

wslongchen / 1500人閱讀

摘要:說一下進(jìn)程線程以及多任務(wù)多進(jìn)程多線程和協(xié)程進(jìn)程概念一個程序?qū)?yīng)一個進(jìn)程,這個進(jìn)程被叫做主進(jìn)程,而一個主進(jìn)程下面還有許多子進(jìn)程。避免了由于系統(tǒng)在處理多進(jìn)程或者多線程時,切換任務(wù)時需要的等待時間。

閱讀本文大約需要 10 分鐘。
14.說一下進(jìn)程、線程、以及多任務(wù)(多進(jìn)程、多線程和協(xié)程)

進(jìn)程

概念
一個程序?qū)?yīng)一個進(jìn)程,這個進(jìn)程被叫做主進(jìn)程,而一個主進(jìn)程下面還有許多子進(jìn)程。

實現(xiàn)方式

fork()
示例:

import os
         
         
print("current_pid :%d" % os.getpid())
     
res = os.fork()
     
# 子進(jìn)程返回的是 0
if res == 0:
print("res: %d" % res)
print("sub_pid: %d" % os.getpid())
     
# 主進(jìn)程返回的是子進(jìn)程的 pid
else:
    print("main_pid: %d" % os.getpid())
    print("res:%d" % res)
     
# 結(jié)果為
current_pid :12775
main_pid: 12775
res:12776
res: 0
sub_pid: 12776multiprocessing.Process

multiprocessing.Process
示例:

from multiprocessing import Process
import os, time
     
     
print("man_process pid : %d" % os.getpid())
     
class NewProcess(Process):
    def __init__(self):
        Process.__init__(self)
     
    def run(self):
        time.sleep(3)
        print("%d process was runing" % os.getpid())
     
np = NewProcess()
np.start()
     
# 結(jié)果為
man_process pid : 7846
7847 process was runing

multiprocessing.Pool

同步(apply)

示例:

from multiprocessing import Pool
import time, os, random
     
     
print("main_process pid: %d" % os.getpid())
     
def run():
    time.sleep(random.random())  # random.random() 隨機(jī)生成一個小于 1 的浮點(diǎn)數(shù)
    print("%d process was runing" % os.getpid())
     
p = Pool(3)
     
for i in range(4):
    p.apply(run, args=())
     
p.close()
print("waiting for sub_process")
     
while True:
    # 獲取 Pool 中剩余的進(jìn)程數(shù)量
    count = len(p._cache)
    if count != 0:
        print("there was %d sub_process" % count)
        time.sleep(random.random())
    else:
        break
             
print("sub_process has done")
     
# 結(jié)果為
main_process pid: 4295
4297 process was runing
4296 process was runing
4298 process was runing
4297 process was runing
wating for sub_process
sub_process has done

異步(apply_async)
示例:

from multiprocessing import Pool
import time, os, random
          
          
print("main_process pid: %d" % os.getpid())
          
def run():
    # random.random() 隨機(jī)生成一個小于 1 的浮點(diǎn)數(shù)
    time.sleep(random.random())  
    print("%d process was runing" % os.getpid())
   
p = Pool(3)
          
for i in range(4):
    p.apply_async(run, args=())
          
    p.close()
          
while True:
    # 獲取 Pool 中剩余的進(jìn)程數(shù)量
    count = len(p._cache)
    if count != 0:
        print("there was %d sub_process" % count)
        time.sleep(random.random())
    else:
        break
                  
print("wiating for sub_process..")
p.join()
          
print("sub_process has done")
          
# 結(jié)果為
main_process pid: 4342
wiating for sub_process..
there was 4 sub_process
4344 process was runing
there was 3 sub_process
4345 process was runing
4344 process was runing
4343 process was runing
sub_process has done

優(yōu)缺點(diǎn)

fork()是計算機(jī)最底層的進(jìn)程實現(xiàn)方式,一個fork()方法創(chuàng)建出來的進(jìn)程有兩個:主進(jìn)程、子進(jìn)程。fork()創(chuàng)建出來的進(jìn)程,主進(jìn)程不會等待子進(jìn)程。

multiprocessing模塊通過將fork方法封裝成一個Process類,該類有一個start()方法,當(dāng)調(diào)用該方法時,會自動調(diào)用run()方法,開啟一個進(jìn)程。并且由Process創(chuàng)建出來的進(jìn)程,可以使用join()方法,使得主進(jìn)程堵塞,被迫等待子進(jìn)程。

multiprocess下另一種開啟進(jìn)程的方式是通過Pool進(jìn)程池來實現(xiàn)。進(jìn)程池可以開啟多個進(jìn)程來執(zhí)行多個任務(wù),但是進(jìn)程數(shù)最大不會超過系統(tǒng) CPU 核數(shù)。同樣的,由Pool創(chuàng)建出來的進(jìn)程,主進(jìn)程也不會等待子進(jìn)程,通過join()方法可以迫使主進(jìn)程等待子進(jìn)程,或者使用apply()同步的方式。

進(jìn)程通信
進(jìn)程之間的通信可以通過隊列(Queue)來進(jìn)行,多個進(jìn)程一部分向隊列里寫入數(shù)據(jù),一部分從隊列里讀取數(shù)據(jù),從而完成多進(jìn)程之間的通信問題。
示例:

from multiprocessing import Process, Queue
import random, time, os
  
  
def write(q):
    if not q.full():
        for i in range(4):
           q.put(i)
           print("%d was writing data[%d] to queue" % (os.getpid(), i))
              time.sleep(random.random())
    else:
        print("queue is full")
  
def read(q):
    # 等待隊列被寫入數(shù)據(jù)
    time.sleep(random.random())
    while True:
        if not q.empty():
            data = q.get()
            print("%d was reading data{%d} from queue" % (os.getpid(), data))
        else:
            print("queue is empty")
            break
      
# 創(chuàng)建通信隊列,進(jìn)程之間,全局變量不共享
q = Queue()
pw = Process(target=write, args=(q,))
pr = Process(target=read, args=(q,))
      
pw.start()
pr.start()
      
pw.join()
pr.join()
print("end")
      
# 結(jié)果為
4640 was writing data[0] to queue
4640 was writing data[1] to queue
4640 was writing data[2] to queue
4641 was reading data{0} from queue
4641 was reading data{1} from queue
4641 was reading data{2} from queue
queue is empty
4640 was writing data[3] to queue
end

由于進(jìn)程的執(zhí)行順序問題,造成了 pr 先于 pw 執(zhí)行,所以 pr 未讀取到數(shù)據(jù),pr 進(jìn)程任務(wù)結(jié)束,堵塞解開,主進(jìn)程繼續(xù)向下運(yùn)行,最后 pw 任務(wù)結(jié)束。

進(jìn)程通信改良
示例:

from multiprocessing import Process, Queue
import random, time, os
    
    
def write(q):
    if not q.full():
        for i in range(4):
            q.put(i)
            print("%d was writing data[%d] to queue" % (os.getpid(), i))
                  time.sleep(random.random())
    else:
        print("queue is full")
    
    def read(q):
        # 等待隊列被寫入數(shù)據(jù)
        time.sleep(random.random())
        while True:
            data = q.get()
            print("%d was reading data{%d} from queue" % (os.getpid(), data))
    
# 創(chuàng)建通信隊列,進(jìn)程之間,沒有全局變量共享之說
q = Queue()
pw = Process(target=write, args=(q,))
pr = Process(target=read, args=(q,))
    
pw.start()
pr.start()
    
pw.join()
# pr 進(jìn)程立刻結(jié)束
pr.terminate()
print("end")
    
# 結(jié)果為
12898 was writing data[0] to queue
12898 was writing data[1] to queue
12898 was writing data[2] to queue
12899 was reading data{0} from queue
12899 was reading data{1} from queue
12899 was reading data{2} from queue
12898 was writing data[3] to queue
12899 was reading data{3} from queue
end

線程

概念
線程是進(jìn)程下的一部分,進(jìn)程下負(fù)責(zé)執(zhí)行代碼程序的就是線程,一個進(jìn)程下會有很多個線程。同樣的,一個主線程下面也有很多子線程。

另外,Python 中的線程依據(jù)的是 Java 中的線程模型,如果有興趣的同學(xué)可以研究一下。

實現(xiàn)方式

示例:

import threading, time
  
  
def run():
    time.sleep(1)
    # currentThread() 返回的是當(dāng)前的線程對象信息
    print("%s was runing" % threading.currentThread())
    print("current thread"name: %s" % threading.currentThread().getName())
  
# 創(chuàng)建一個線程
t = threading.Thread(target=run, args=())
  
# 啟動線程
t.start()
  
# get_ident 返回的是當(dāng)前線程對象所在的內(nèi)存地址(id),該地址是唯一可以驗證線程的數(shù)據(jù)
# 也可使用 currentThread().getName() 來簡單的區(qū)分線程
print("current thread"name: %s" % threading.currentThread().getName())
print("main_thread tid: %s" % threading.get_ident())
  
# 結(jié)果為
current thread"name: MainThread
main_thread tid: 140427132020480
 was runing
current thread"name: Thread-1

線程通信

通信隊列
通信隊列作為相對來說最為安全的線程通信手段,其中Queue模塊自身擁有所有所需的鎖,這使得通信隊列中的對象可以安全的在多線程之間共享。

這里用常見的「生產(chǎn)者-消費(fèi)者模型」來介紹。

示例:

import threading, queue, time, random
    
flag = object()
    
def producter(q):
    for i in range(4):
        q.put(i)
    print("%s put data{%d} in queue" % (threading.currentThread().getName(), i))
    time.sleep(random.random())
    q.put(flag)
    
def consumer(q):
    time.sleep(random.random())
    while True:
        res = q.get()
        if res == flag:
            q.put(flag)
            break
        else:
            print("%s get data{%d} from queue" % (threading.currentThread().getName(), res))
    
# 創(chuàng)建隊列
q = queue.Queue()
    
# 創(chuàng)建線程
pro = threading.Thread(target=producter, args=(q,))
con = threading.Thread(target=consumer, args=(q,))
    
pro.start()
con.start()
    
# 結(jié)果為
Thread-1 put data{0} in queue
Thread-1 put data{1} in queue
Thread-2 get data{0} from queue
Thread-2 get data{1} from queue
Thread-1 put data{2} in queue
Thread-2 get data{2} from queue
Thread-1 put data{3} in queue
Thread-2 get data{3} from queue
end

這里有一個細(xì)節(jié)。在多線程下,當(dāng)生產(chǎn)者任務(wù)完成之后,向隊列queue里添加了一個特殊對象(終止信號)flag,這樣當(dāng)消費(fèi)者從queue中取出任務(wù)時,當(dāng)取到flag時,意味著所有任務(wù)被取出,并再次將flag添加至queue中,這樣其他線程中的消費(fèi)者在接收到這個終止信號后,也會得知當(dāng)前生產(chǎn)者任務(wù)已經(jīng)全部發(fā)布。

輪詢
通過為數(shù)據(jù)操作添加while循環(huán)判斷,迫使線程被迫等待操作。(為了優(yōu)化等待時間,應(yīng)在最核心的位置添加判斷條件)

示例:

import threading
        
        
class NewThread(threading.Thread):
    flag = 0
    g_num = 0
        
    def __init__(self):
         super().__init__()
        
    def run(self):
        print("%s was runing" % threading.currentThread().getName())
        if self.name == "Thread-1":
            self.add_num()
            NewThread.flag = 1
        else:
            # 輪詢
            # Thread-2 被迫等待 Thread-1 完成任務(wù)之后才能執(zhí)行
            while True:
                if NewThread.flag:
                    self.add_num()
                    break
        
    @classmethod
    def add_num(cls):
        global g_num
        for i in range(1000000):
            cls.g_num += 1
        print("on the %s, g_num: %d" % (threading.currentThread().getName(), cls.g_num))
        
t1 = NewThread()
t2 = NewThread()
        
t1.start()
t2.start()
        
# 結(jié)果為
Thread-1 was runing
Thread-2 was runing
on the Thread-1, g_num: 1000000
on the Thread-2, g_num: 2000000

互斥鎖
互斥鎖是專門為了針對線程安全而設(shè)計的一種結(jié)構(gòu),鎖可以強(qiáng)制線程排序,保護(hù)線程安全,但是加鎖、解鎖會消耗系統(tǒng) CPU 資源。

互斥鎖優(yōu)化

示例:

import threading
      
      
class NewThread(threading.Thread):
    g_num = 0
    # 生成鎖對象
    lock = threading.Lock()
      
    def __init__(self):
         super().__init__()
      
         def run(self):
               # 判斷當(dāng)前線程是否上鎖,若未上鎖,則一直嘗試上鎖(acquire)直至成功
             with NewThread.lock:
                 print("%s was runing" % self.name)
                 self.add_num()
      
         @classmethod
         def add_num(cls):
             for i in range(1000000):
                 cls.g_num += 1
             print("on the %s g_num: %d" % (threading.currentThread().getName(), cls.g_num))
      
t1 = NewThread()
t2 = NewThread()
      
t1.start()
t2.start()
      
# 結(jié)果為
Thread-1 was runing
on the Thread-1 g_num: 1000000
Thread-2 was runing
on the Thread-2 g_num: 2000000

死鎖問題
當(dāng)多線程下出現(xiàn)多個鎖,判斷條件又是另一個線程里的鎖時,就會出現(xiàn)一種情況:當(dāng)另一個線程任務(wù)執(zhí)行時間過長,或是線程結(jié)束,未解鎖。當(dāng)前線程由于遲遲無法上鎖,程序始終阻塞,此時就會陷入死鎖問題。

死鎖問題解決

設(shè)置超時時間threading.Lock().acquire(timeout=3)只要在上鎖時設(shè)置超時時間timeout=,只要超過時間,線程就會不再等待是否解鎖,而是直接運(yùn)行。但是這種方式很危險,可能會帶來大量的等待時間。

為每個鎖添加一個特殊編號,多線程在獲取鎖的時候嚴(yán)格按照該編號的升序方式來獲取,相當(dāng)于為線程排序,這樣就避免了多線程因為資源爭搶,而陷入死鎖的可能。

銀行家算法

進(jìn)程與線程的區(qū)別

線程和進(jìn)程的執(zhí)行順序都是一樣的,都是由操作系統(tǒng)的調(diào)度算法決定,不是根據(jù)程序的編寫順序來決定。

進(jìn)程是資源分配的單位,而線程是 CPU 調(diào)度的單位。

進(jìn)程在主程序結(jié)束后,程序立馬結(jié)束,需要手動利用join()方法使得主程序發(fā)生堵塞,來等待子進(jìn)程。而主線程的任務(wù)結(jié)束后,程序會等待子線程結(jié)束才會結(jié)束。故不需要特意使用join()方法來使主線程等待子線程。

多進(jìn)程適合 CPU 密集型,多線程適合 I/O 密集型。

協(xié)程

概念
線程下的一種,也叫微線程,單線程自身控制切換任務(wù)時機(jī),達(dá)到多任務(wù)的效果。避免了由于系統(tǒng)在處理多進(jìn)程或者多線程時,切換任務(wù)時需要的等待時間。這一點(diǎn)很像操作系統(tǒng)里的中斷。

實現(xiàn)方式

生成器(yield)
生成器相關(guān)內(nèi)容可看問題 13。

這里以一個簡單的「生產(chǎn)者-消費(fèi)者模型」來解釋如何使用生成器實現(xiàn)協(xié)程。

示例:

import threading
     
     
def producter(c):
    next(c)
    n = 4
    print("%s was running" % threading.currentThread().getName())
    
    while n:
        print("product data: %d" % n)
        res = c.send(n)
        print(res)
        n -= 1
    print("sale out")
     
     
def consumer():
    res = ""
     
    print("%s was running" % threading.currentThread().getName())
    while True:
        n = yield res
     
        print("consume data: %d" % n)
        res = "200 OK"
     
print("%s was running" % threading.currentThread().getName())
c = consumer()
     
producter(c)
     
# 結(jié)果為
MainThread was running
MainThread was running
MainThread was running
product data: 4
consume data: 4
200 OK
product data: 3
consume data: 3
200 OK
product data: 2
consume data: 2
200 OK
product data: 1
consume data: 1
200 OK
sale out

可以看到,生產(chǎn)者事先不知道消費(fèi)者具體要消費(fèi)多少數(shù)據(jù),生產(chǎn)者只是一直在生產(chǎn)。而消費(fèi)者則是利用生成器的中斷特性,consumer函數(shù)中,程序每一次循環(huán)遇到yield關(guān)鍵字就會停下,等待producter函數(shù)啟動生成器,再繼續(xù)下一次循環(huán)。

在這中間只有一個線程在運(yùn)行,任務(wù)的切換時機(jī)由程序員自己控制,避免了由于多線程之間的切換消耗,這樣就簡單實現(xiàn)了協(xié)程。

異步 I/O(asyncio)
由于生成器在未來的 Python 3.10 版本中將不在支持協(xié)程,而是推薦使用asyncio庫,該庫適用于高并發(fā)。

自己目前不會,就不瞎 BB 了,具體可看文檔。

asyncio 中文文檔

未寫完,下次更新補(bǔ)上

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/45144.html

相關(guān)文章

  • http相關(guān)面試

    摘要:狀態(tài)碼有那些分別代表是什么意思簡單版繼續(xù),一般在發(fā)送請求時,已發(fā)送了之后服務(wù)端將返回此信息,表示確認(rèn),之后發(fā)送具體參數(shù)信息正常返回信息請求成功并且服務(wù)器創(chuàng)建了新的資源服務(wù)器已接受請求,但尚未處理請求的網(wǎng)頁已永久移動到新位置。 http狀態(tài)碼有那些?分別代表是什么意思? 簡單版 [ 100 Continue 繼續(xù),一般在發(fā)送post請求時,已發(fā)送了http header之后...

    沈建明 評論0 收藏0
  • 2018前端面試題匯總(更新...)

    摘要:方法一因為是從開始的方法二獲取怎么實現(xiàn)和截取考察的用法。翻轉(zhuǎn)字符串和刪除數(shù)組的第一元素將字符串轉(zhuǎn)化為數(shù)組。將數(shù)組進(jìn)行翻轉(zhuǎn)。將數(shù)組轉(zhuǎn)換為字符串。被刪除的第一個元素刪除后的數(shù)組數(shù)組去重如果找到不到就把放到新數(shù)組里 1.運(yùn)算題的結(jié)果 var name=jay var pe={ name:kang, getname:function () { ...

    smartlion 評論0 收藏0
  • 關(guān)于三次握手與四次揮手面試官想考我們什么?--- 不看后悔系列

    摘要:第三次握手客戶端收到報文之后,會回應(yīng)一個報文。因此,需要三次握手才能確認(rèn)雙方的接收與發(fā)送能力是否正常。三次握手的作用三次握手的作用也是有好多的,多記住幾個,保證不虧。也就是說,第一次第二次握手不可以攜帶數(shù)據(jù),而第三次握手是可以攜帶數(shù)據(jù)的。在面試中,三次握手和四次揮手可以說是問的最頻繁的一個知識點(diǎn)了,我相信大家也都看過很多關(guān)于三次握手與四次揮手的文章,今天的這篇文章,重點(diǎn)是圍繞著面試,我們應(yīng)該...

    WilsonLiu95 評論0 收藏0
  • 2018年, 我的前端面試復(fù)盤

    摘要:技術(shù)一面一面主要考察基礎(chǔ),有些會有技術(shù)筆試,比如騰訊,。騰訊的面試官就很喜歡問,安全,瀏覽器緩存方面的問題,計算機(jī)基礎(chǔ),但是要懂為什么。 這篇文章簡單總結(jié)下2018年內(nèi)我的一些前端面試經(jīng)歷, 在這簡單分享一下,希望對大家有所啟發(fā)。 樓主在深圳,畢業(yè)兩年。面的主要是深圳的幾家公司。 包括: 騰訊, 螞蟻金服, Lazada, Shopee, 有贊 等 。 樓主在準(zhǔn)備面試前, 想著復(fù)習(xí)一...

    Yujiaao 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<