Python 並發部分的面試題


進程

  • 進程間內存是否共享?如何實現通訊?
    進程間內存不共享,可以通過
    1. Manage模塊加鎖
    2. 通過隊列或
    3. 通過管道加鎖
    4. socket實現通訊
  • 請聊聊進程隊列的特點和實現原理?
    1. 先進先出 Queue
    2. 后進先出 LifoQueue
    3. 優先級隊列 PriorityQueue
    4. 線程本身帶鎖通過put()數據和get()數據,同一時間只有一個線程運行修改任務實現數據安全
  • 請畫出進程的三狀態轉換圖
    就緒====運行
       \阻塞/
  • 你了解生產者模型消費者模型么?如何實現?
yield:
def consumer():
    n = yield
    n = yield
    n = yield
    n = yield
    n = yield
    n = yield
    n = yield
    n = yield

def producer():
    g = consumer()
    next(g)
    for i in range(2000000):
        g.send(i)
  • 從你的角度說說進程在計算機中扮演什么角色?
    進程在計算機中扮演數據集的調用調配角色。
    負責回收和控制子線程的運行,是一個數據集。
    ##線程
  • GIL鎖是怎么回事?
    GIL鎖是全局解釋器鎖,只有CPython中使用,同一時間只能有一個線程調度CPU
  • 在python中是否線程安全?
    不安全,需要加鎖才安全
  • 什么叫死鎖?
    同時滿足兩個條件鎖才能解開,分別有兩把或以上的鎖,有多個線程分別搶占了兩個條件中的鎖,互不釋放造成阻塞,死鎖現象。
  • logging模塊是否是線程安全的?
    logging模塊是線程安全的,因為使用的是單例設計模式。
  • threading.local的作用?
    Python提供了 threading.local 類,將這個類實例化得到一個全局對象,
    但是不同的線程使用這個對象存儲的數據其它線程不可見(本質上就是不同的線程使用這個對象時為其創建一個獨立的字典)。
  • 程序從flaga執行到falgb的時間大致是多少秒?
    60s 因為沒有設置守護線程,需要等子線程跑完主線程才結束
import threading
import time

def _wait():
    time.sleep(60)
# flag a
t = threading.Thread(target=_wait, daemon=False)
t.start()
# flag b
  • 程序從flaga執行到falgb的時間大致是多少秒?
    0.01s 因為設置守護線程,子線程等待所有非子線程結束子線程結束。
import threading
import time

def _wait():
    time.sleep(60)
# flag a
t = threading.Thread(target=_wait, daemon=True)
t.start()
# flag b
  • 程序從flaga執行到falgb的時間大致是多少秒?
    60s 因為設置了阻塞需要等待子線程結束主線程才能結束
import threading
import time

def _wait():
    time.sleep(60)
# flag a
t = threading.Thread(target=_wait, daemon=True)
t.start()
t.join()
# flag b
  • 讀程序,請確認執行到最后number是否一定為0
    不一定,因為存在線程安全問題,同一時間修改變量值
import threading
loop = int(1E7)

def _add(loop: int = 1):
    global number
    for _ in range(loop):
        number += 1

def _sub(loop: int = 1):
    global number
    for _ in range(loop):
        number -= 1
number = 0

ta = threading.Thread(target=_add, args=(loop,))
ts = threading.Thread(target=_sub, args=(loop,))
ta.start()
ts.start()
ta.join()
ts.join()
print(number)
  • 讀程序,請確認執行到最后number是否一定為0
    一定為0 ,因為增刪操作只有一條語句不會產生數據安全問題。
import threading
loop = int(1E7)
def _add(loop: int = 1):
    global number
    for _ in range(loop):
        number += 1

def _sub(loop: int = 1):
    global number
    for _ in range(loop):
        number -= 1
number = 0
ta = threading.Thread(target=_add, args=(loop,))
ts = threading.Thread(target=_sub, args=(loop,))
ta.start()
ta.join()
ts.start()
ts.join()
print(number)
  • 讀程序,請確認執行到最后number的長度是否一定為1
    不一定為1,因為產生了阻塞可能有數據安全問題。
import threading
loop = int(1E7)

def _add(loop: int = 1):
    global numbers
    for _ in range(loop):
        numbers.append(0)

def _sub(loop: int = 1):
    global number
    while not numbers:
        time.sleep(1E-8)
    numbers.pop()
numbers = [0]
ta = threading.Thread(target=_add, args=(loop,))
ts = threading.Thread(target=_sub, args=(loop,))
ta.start()
ta.join()
ts.start()
ts.join()
print(numbers)
  • 讀程序,請確認執行到最后number的長度是否一定為1
    不一定為1,因為產生了阻塞可能有數據安全問題。
import threading
loop = int(1E7)

def _add(loop: int = 1):
    global numbers
    for _ in range(loop):
        numbers.append(0)

def _sub(loop: int = 1):
    global number
    while not numbers:
        time.sleep(1E-8)
    numbers.pop()
numbers = [0]
ta = threading.Thread(target=_add, args=(loop,))
ts = threading.Thread(target=_sub, args=(loop,))
ta.start()
ts.start()
ta.join()
ts.join()
print(numbers)

協程

  • 什么是協程?常用的協程模塊有哪些?
    在線程遇到阻塞的情況時切換到另一個線程繼續執行,如果遇到阻塞再到另一個線程執行。如果跳轉過的線程轉為就緒狀態就直接執行,
    知道程序中沒有阻塞。有gevent,和greenlet.
  • 協程中的join是用來做什么用的?它是如何發揮作用的?
    協程是遇到阻塞才切換,join負責阻塞開始執行程序。
  • 使用協程實現並發的tcp
server端
from gevent import monkey;monkey.patch_all()
import gevent
import time
import random
import socket

sk = socket.socket()
server_addr = ('127.0.0.1',9988)
sk.bind(server_addr)
sk.listen()

def listen(conn):
    while True:
        msg = conn.recv(1024).decode('utf-8')
        if msg == 'q':break
        conn.send(msg.upper().encode('utf-8'))

if __name__ == '__main__':
    while True:
        conn,addr = sk.accept()
        gevent.spawn(listen,conn)
  • 在一個列表中有多個url,請使用協程訪問所有url,將對應的網頁內容寫入文件保存
from gevent import monkey;monkey.patch_all()
import time
import random
import gevent
from urllib.request import urlopen
url_dic = {
   '協程':'http://www.cnblogs.com/Eva-J/articles/8324673.html',
   '線程':'http://www.cnblogs.com/Eva-J/articles/8306047.html',
   '目錄':'https://www.cnblogs.com/Eva-J/p/7277026.html',
   '百度':'http://www.baidu.com',
   'sogou':'http://www.sogou.com',
   '4399':'http://www.4399.com',
   '豆瓣':'http://www.douban.com',
   'sina':'http://www.sina.com.cn',
   '淘寶':'http://www.taobao.com',
   'JD':'http://www.JD.com'
}
def getHtml(url):
   ret = urlopen(url)
   html = ret.read()
   html_file = url.replace('.','_').lstrip('https://').replace('/','_')+'.html'
   with open(html_file,mode='wb') as f:
       f.write(html)
   print(url+'done')

if __name__ == '__main__':
    print('start')
    lst = list()
    for url in url_dic:
       ret = gevent.spawn(getHtml,url_dic[url])
       lst.append(ret)

    for url in lst:
       url.join()
    print(1)

綜合

  • 進程和線程的區別
    進程是計算機資源分配的最小單位,線程是cpu調度的最小單位,線程較進程來說較輕量,啟動更迅速。調用更方便
  • 進程池、線程池的優勢和特點
    進程池一般在高計算的條件下使用,資源消耗更多。
    線程池可以運行在一個進程中,執行效率更高,可控cpu使用率。節約資源。
  • 線程和協程的異同?
    協程,即協作式程序,其思想是,一系列互相依賴的協程間依次使用CPU,每次只有一個協程工作,而其他協程處於休眠狀態。
    協程實際上是在一個線程中,只不過每個協程對CUP進行分時,協程可以訪問和使用unity的所有方法和component
    線程,多線程是阻塞式的,每個IO都必須開啟一個新的線程,但是對於多CPU的系統應該使用thread,尤其是有大量數據運算的時刻,
    但是IO密集型就不適合;而且thread中不能操作unity的很多方法和component
  • 請簡述一下互斥鎖和遞歸鎖的異同?
    兩者在在單鎖的情況下都可以保證數據的可靠性。
    二者唯一的區別是,同一個線程可以多次獲取同一個遞歸鎖,不會產生死鎖。而如果一個線程多次獲取同一個非遞歸鎖,則會產生死鎖。
  • 請列舉一個python中數據安全的數據類型?
    Queue
  • Python中如何使用線程池和進程池
from concurrent.futures import ThreadPoolExecutor
from concurrent.futures import ProcessPoolExector
p = ProcessPoolExector(4)
ret = p.submit(func,aa)
print(ret.result())
  • 簡述
    進程、線程、協程的區別
    進程是:一個CPU情況下,多個程序分別使用機器資源(CPU或硬盤等)的概念;
    線程是:一個進程情況下,多個執行流程(即線程)分別使用分配給該進程的機器資源的概念;
    • 協程是:一個線程情況下,多個執行流程(即協程)由線程控制,分別使用分配給該線程的機器資源的概念;
      以及應用場景?
    • 多進程:密集CPU任務,需要充分使用多核CPU資源(服務器,大量的並行計算)的時候,用多進程。 multiprocessing
      缺陷:多個進程之間通信成本高,切換開銷大。
    • 多線程:密集I/O任務(網絡I/O,磁盤I/O,數據庫I/O)使用多線程合適。
      threading.Thread、multiprocessing.dummy
      缺陷:同一個時間切片只能運行一個線程,不能做到高並行,但是可以做到高並發。
    • 多線程請求返回是無序的,那個線程有數據返回就處理那個線程,而協程返回的數據是有序的。
      缺陷:單線程執行,處理密集CPU和本地磁盤IO的時候,性能較低。處理網絡I/O性能還是比較高.
  • 什么是並行,什么是並發?
    並行是同一時間在多個cpu上同時執行多個程序
    並發是同一時間在同一個cpu上在不同程序之間切換運行
  • 請解釋同步和異步這兩個概念?
    同步執行程序需要等待結果
    異步執行程序不需要等待結果
  • 請談談對異步非阻塞的了解?
    執行效率高,可以實現高並發的應用場景
  • 簡述信號量的實現原理
    • 信號量機制即利用pv操作來對信號量進行處理。什么是信號量?信號量(semaphore)的數據結構為一個值和一個指針,指針指向等待該信號量的下一個進程。信號量的值與相應資源的使用情況有關。當它的值大於0時,表示當前可用資源的數量;
    • 當它的值小於0時,其絕對值表示等待使用該資源的進程個數。注意,信號量的值僅能由PV操作來改變。一般來說,信號量S³0時,
    • S表示可用資源的數量。執行一次P操作意味着請求分配一個單位資源,因此S的值減1;當S<0時,表示已經沒有可用資源,
    • 請求者必須等待別的進程釋放該類資源,它才能運行下去。而執行一個V操作意味着釋放一個單位資源,因此S的值加1;若S£0,
    • 表示有某些進程正在等待該資源,因此要喚醒一個等待狀態的進程,使之運行下去。
  • 程序中的阻塞有哪些?給程序帶來了哪些影響?
    input,sleep,io 程序會等待阻塞完成才會進入就緒隊列,影響程序運行效率
  • 請分別用多進程、多線程、協程實現生產者消費者模型?

多進程

import time
import random
from multiprocessing import Process,Queue

def producer(name,que):
    for i in range(10):
        time.sleep(random.random())
        food = '便便💩'
        print('%s 生產了一陀%s' % (name,food))
        que.put(food)

def consumer(name,que):
    while True:
        time.sleep(random.random())
        food = que.get()
        if not food: break
        print('%s 吃了一陀%s' % (name,food))

if __name__ == '__main__':
    p_l = list()
    que = Queue()
    p = Process(target=producer,args=('baoyuan',que))
    p.start()
    p.join()
    c = Process(target=consumer,args=('alex',que,))
    c.start()
    que.put(None)
    c.join()
    print('全部吃完啦。')

多線程

import time
import random
from queue import Queue
from threading import Thread

def producer(name,que):
    for i in range(10):
        time.sleep(random.random())
        food = '便便💩'
        print('%s 生產了一陀%s' % (name,food))
        que.put(food)

def consumer(name,que):
    while True:
        time.sleep(random.random())
        food = que.get()
        if not food: break
        print('%s 吃了一陀%s' % (name,food))

if __name__ == '__main__':
    p_l = list()
    que = Queue()
    p = Thread(target=producer,args=('baoyuan',que))
    p.start()
    p.join()
    c = Thread(target=consumer,args=('alex',que,))
    c.start()
    que.put(None)
    c.join()
    print('全部吃完啦。')

協程

from gevent import monkey;monkey.patch_all()
from queue import Queue
import gevent
import random
import time

def producer(name,que):
    for i in range(10):
        time.sleep(random.random())
        food = '便便💩'
        print('%s 生產了一陀%s' % (name,food))
        que.put(food)

def consumer(name,que):
    while True:
        time.sleep(random.random())
        food = que.get()
        if not food: break
        print('%s 吃了一陀%s' % (name,food))

if __name__ == '__main__':
    p_l = list()
    que = Queue()
    p = gevent.spawn(producer,'baoyuan',que)
    p.join()
    c = gevent.spawn(consumer,'alex',que)
    que.put(None)
    c.join()
    print('全部吃完啦。')


免責聲明!

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



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