作業8-12

python下劃線的作用

單下劃線 _

作爲一個臨時或者無關緊要的變量

a, _, _, b = (1, 2, 3, 4)
print(a, b)  # 1 4

前導單下劃線 _test

命名約定,告知其他程序員該變量或者方法僅供內部使用

不會對python解釋器的行爲產生影響

前導雙下劃線 __test

前導雙下劃線會讓python解釋器重寫屬性名稱,可以避免子類中的命名衝突

class A:
    def runs(self):
        pass
class B(A):
    def __runs(self):
        pass
a = A()
b = B()
print(dir(a))
print(dir(b))

out:

['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'runs']
['_B__runs', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'runs']

末尾單下劃線 test_

當變量名與關鍵字衝突時,加一個下劃線解決衝突

雙前導雙末尾下劃線 __test__

被雙前導雙末尾下劃線修飾的名稱被用作特殊用途,如__init__等,這些方法被稱爲神奇方法或者魔術方法。

在自己的程序中避免使用這種方法來命名自己的變量,python解釋器的後續更新可能會使用該變量。

三次握手及四次揮手

三次握手

  • 客戶端發送SYN報文交給服務器端,進入SYN_SENT狀態。
  • 服務器端收到SYN報文,迴應一個SYN+ACK報文,進入SYN_RECV狀態。
  • 客戶端收到服務器端的SYN報文,迴應一個ACK報文,客戶端和服務器進入ESTABLISHED狀態。

四次揮手

  • 某個應用進程首先調用close,稱該端執行主動關閉。該端的TCP發送一個FIN表示數據發送完畢。
  • 接收到這個FIN的對端執行被動關閉,迴應一個ACK表示確認。
  • 一段時間後,對端發送完所有數據,調用close,發送一個FIN
  • 該端接收到FIN,迴應一個ACK。

線程進程的關係

  • 同一進程中的線程共享同一內存空間,但是進程之間是獨立的。
  • 同一個進程中的所有線程的數據是共享的,進程之間的數據是獨立的。
  • 對主線程的修改可能影響其他線程的行爲,但是父進程的修改(除了刪除)不會影響其他子進程。
  • 線程是一個上下文的執行指令,而進程則是與運算相關的一簇資源。
  • 同一個進程的線程之間可以直接通信,但是進程之間的交流需要藉助中間代理來實現。
  • 創建新的線程很容易,但是創建新的進程需要對父進程做一次複製。
  • 一個線程可以操作同一進程的其他線程,但是進程只能操作其子進程。
  • 線程啓動速度快,進程啓動速度慢(但是兩者運行速度沒有可比性)。

GIL

GIL(Global Interpreter Lock):全局解釋器鎖

每個線程在執行的過程中都需要先獲取GIL,保證同一時刻只有一個線程可以執行代碼。

GIL並不是python的特性,GIL屬於cpython。

隨着多核CPU的出現,爲了有效利用多核CPU的性能,就出現了多線程的編程方式,而隨之以來的是線程間數據一致性和狀態同步的困難。

線程是非獨立的,所以同一進程裏線程是數據共享的,當各個線程訪問數據資源時會出現競爭狀態,即數據可能同時被多個線程佔用,造成數據混亂,這就是線程的不安全。

demo:

import threading

a = 0


def thread_run():
    global a
    for i in range(1000000):
        a += 1
    print(a)


def main():
    t1 = threading.Thread(target=thread_run)
    t2 = threading.Thread(target=thread_run)
    t1.start()
    t2.start()


if __name__ == '__main__':
    main()
# 1357088
# 1511505

可以發現結果並不是預期的2000000,這是由於資源競爭產生的問題。

雖然有了GIL但還是出現了上述資源競爭的問題,這是由於GIL保護的是解釋器級別的數據,如垃圾回收的數據,用戶開發的程序的數據不受GIL保護,上述代碼僅用於說明資源競爭問題

原子操作就是不會因爲進程併發或者線程併發而導致被中斷的操作。原子操作的特點就是要麼一次全部執行,要麼全不執行。不存在執行了一半而被中斷的情況。

當對全局資源存在寫操作時,如果不能保證寫入過程的原子性,會出現髒讀髒寫的情況。

上述代碼中的a+=1並不是原子的,它實際經過了三步:

  1. 讀入a變量指向的值
  2. +1
  3. 讓a變量指向新的結果值

如果在這三步之間被解釋器打斷奪走GIL就會導致更新丟失,爲了解決這個問題需要再人工加鎖。

與之相似,爲了解決多線程之間數據完整性和狀態同步的問題,GIL誕生了。

GIL保證了任意時刻只有一個線程再解釋器中運行。即Python中的多線程是表面上的多線程,是併發執行而並不是並行執行。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章