python多線程

GIL

global interpreter lock(cpython)html

同一時刻只有一個線程運行在一個cpu上執行字節碼(沒法將多個線程映射到多個cpu上)python

import dis

def add(a):
    a = a + 1
    return a

print(dis.dis(add))

GIL在某些狀況下會釋放

每次的結果都不同 線程之間的安全問題編程

GIL會根據執行的直接碼行數或者時間片釋放GIL安全

遇到IO操做時主動釋放多線程

total = 0

def add():
    #1. dosomething1
    #2. io操做
    # 1. dosomething3
    global total
    for i in range(1000000):
        total += 1
def desc():
    global total
    for i in range(1000000):
        total -= 1

import threading
thread1 = threading.Thread(target=add)
thread2 = threading.Thread(target=desc)
thread1.start()
thread2.start()

thread1.join()
thread2.join()
print(total)

多線程編程

操做系統可以調度的的最小單位是進程,由於進程對系統的資源消耗很是大,因此後期就演變成了線程,線程其實是依賴於咱們的進程(任務管理器中咱們實際上能看到的實際上是進程 ),操做系統能調度的最小單元是線程。性能

對於io操做爲主的編程來講,多進程和多先產出的性能差異不大,甚至多線程比多進程的性能還高,由於多線程編程更加輕量級。ui

簡單的線程

import time
from threading import Thread


def get_detail_html(url):
    print("get detail html started")
    time.sleep(2)
    print("get detail html end")


def get_detail_url(url):
    print("get detail url started")
    time.sleep(4)
    print("get detail url end")


if __name__ == '__main__':
    thread1 = Thread(target=get_detail_html, args=("",))
    thread2 = Thread(target=get_detail_url, args=("",))
    # 設置爲守護線程 當主線程運行完時 子線程被kill掉
    thread1.setDaemon(True)
    thread2.setDaemon(True)
    start_time = time.time()
    thread1.start()
    thread2.start()

    # 設置爲阻塞 等待線程運行完再關閉主線程
    thread1.join()
    thread2.join()
    # 默認狀況下 主線程退出與時 子線程不會被kill掉
    print("last time: {}".format(time.time() - start_time))

重載線程實現多線程

import time
import threading

def get_detail_html(url):
    print("get detail html started")
    time.sleep(2)
    print("get detail html end")


def get_detail_url(url):
    print("get detail url started")
    time.sleep(4)
    print("get detail url end")


#2. 經過集成Thread來實現多線程
class GetDetailHtml(threading.Thread):
    def __init__(self, name):
        super().__init__(name=name)

    def run(self):
        print("get detail html started")
        time.sleep(2)
        print("get detail html end")


class GetDetailUrl(threading.Thread):
    def __init__(self, name):
        super().__init__(name=name)

    def run(self):
        print("get detail url started")
        time.sleep(4)
        print("get detail url end")

if  __name__ == "__main__":
    thread1 = GetDetailHtml("get_detail_html")
    thread2 = GetDetailUrl("get_detail_url")
    start_time = time.time()
    thread1.start()
    thread2.start()

    thread1.join()
    thread2.join()

    #當主線程退出的時候, 子線程kill掉
    print ("last time: {}".format(time.time()-start_time))

多線程之間的通訊

使用queueurl

# filename: thread_queue_test.py
# 經過queue的方式進行線程間同步
from queue import Queue
import time
import threading


def get_detail_html(queue):
    # 死循環 爬取文章詳情頁
    while True:
        url = queue.get()
        # for url in detail_url_list:
        print("get detail html started")
        time.sleep(2)
        print("get detail html end")


def get_detail_url(queue):
    # 死循環 爬取文章列表頁
    while True:
        print("get detail url started")
        time.sleep(4)
        for i in range(20):
            # put 等到有空閒位置 再放入
            # put_nowait 非阻塞方式
            queue.put("http://projectsedu.com/{id}".format(id=i))
        print("get detail url end")


# 1. 線程通訊方式- 共享變量
if __name__ == "__main__":
    detail_url_queue = Queue(maxsize=1000)

    thread_detail_url = threading.Thread(target=get_detail_url, args=(detail_url_queue,))
    for i in range(10):
        html_thread = threading.Thread(target=get_detail_html, args=(detail_url_queue,))
        html_thread.start()

    start_time = time.time()
    # 調用task_down從主線程退出
    detail_url_queue.task_done()
    # 從queue的角度阻塞
    detail_url_queue.join()

    print("last time: {}".format(time.time() - start_time))

線程的同步問題

在多線程編程中必需要面對的問題操作系統

無鎖不安全的緣由

# 沒有鎖
def add1(a):
    a += 1

def desc1(a):
    a -= 1

"""add
1. load a  a = 0
2. load 1  1
3. +    1
4. 賦值給a a=1
"""

"""add
1. load a  a = 0
2. load 1  1
3. -    1
4. 賦值給a a=-1
"""
import dis
print(dis.dis(add1))
print(dis.dis(desc1))

普通鎖(Lock)

用鎖會影響性能,鎖會引發死鎖(兩次獲取鎖,獲取鎖以後不釋放,互相等待(a須要b的資源 b須要a的資源))線程

import threading
from threading import Lock

total = 0
# 定義一把鎖
lock = Lock()
def add():
    global total
    global lock
    for i in range(1000000):
        # 獲取鎖
        lock.acquire()
        total += 1
        # 釋放鎖
        lock.release()

def desc():
    global total
    for i in range(1000000):
        lock.acquire()
        total -= 1
        lock.release()

thread1 = threading.Thread(target=add)
thread2 = threading.Thread(target=desc)
thread1.start()
thread2.start()

thread1.join()
thread2.join()
print(total)

相互等待(資源競爭)

"""
A(a、b)
acquire (a)
acquire (b)

B(a、b)
acquire (b)
acquire (a)

# 解決辦法
B(a、b)
acquire (a)
acquire (b)
"""

可重入鎖(Rlock)

import threading
from threading import RLock

total = 0
# 可重入鎖 能夠在同一個線程中可載入屢次
lock = RLock()
def add(lock):
    global total
    for i in range(1000000):
        # 獲取鎖
        lock.acquire()
        lock.acquire()
        total += 1
        do_something(lock)
        # 釋放鎖
        lock.release()
        lock.release()

def desc():
    global total
    for i in range(1000000):
        lock.acquire()
        total -= 1
        lock.release()

def do_something(lock):
    lock.acquire()
    # do something
    lock.release()

thread1 = threading.Thread(target=add)
thread2 = threading.Thread(target=desc)
thread1.start()
thread2.start()

thread1.join()
thread2.join()
print(total)

條件變量鎖(condition)

用於複雜的線程間同步

# 沒有條件鎖 不能實現對話
import threading


class XiaoAi(threading.Thread):
   def __init__(self, lock):
       super().__init__(name="小愛")
       self.lock = lock

   def run(self):
       self.lock.acquire()
       print("{} : 在 ".format(self.name))
       self.lock.release()

       self.lock.acquire()
       print("{} : 好啊 ".format(self.name))
       self.lock.release()


class TianMao(threading.Thread):
   def __init__(self, lock):
       super().__init__(name="天貓精靈")
       self.lock = lock

   def run(self):
       self.lock.acquire()
       print("{} : 小愛同窗 ".format(self.name))
       self.lock.release()

       self.lock.acquire()
       print("{} : 咱們來對古詩吧 ".format(self.name))
       self.lock.release()


if __name__ == "__main__":
   cond = threading.Condition()
   xiaoai = XiaoAi(cond)
   tianmao = TianMao(cond)

   xiaoai.start()
   tianmao.start()
# 條件鎖
import threading


class XiaoAi(threading.Thread):
    def __init__(self, cond):
        super().__init__(name="小愛")
        self.cond = cond

    def run(self):
        with self.cond:
            self.cond.wait()
            print("{} : 在 ".format(self.name))
            self.cond.notify()

            self.cond.wait()
            print("{} : 好啊 ".format(self.name))
            self.cond.notify()

            self.cond.wait()
            print("{} : 君住長江尾 ".format(self.name))
            self.cond.notify()

            self.cond.wait()
            print("{} : 共飲長江水 ".format(self.name))
            self.cond.notify()

            self.cond.wait()
            print("{} : 此恨什麼時候已 ".format(self.name))
            self.cond.notify()

            self.cond.wait()
            print("{} : 定不負相思意 ".format(self.name))
            self.cond.notify()

class TianMao(threading.Thread):
    def __init__(self, cond):
        super().__init__(name="天貓精靈")
        self.cond = cond

    def run(self):
        with self.cond:
            print("{} : 小愛同窗 ".format(self.name))
            self.cond.notify()
            self.cond.wait()

            print("{} : 咱們來對古詩吧 ".format(self.name))
            self.cond.notify()
            self.cond.wait()

            print("{} : 我住長江頭 ".format(self.name))
            self.cond.notify()
            self.cond.wait()

            print("{} : 日日思君不見君 ".format(self.name))
            self.cond.notify()
            self.cond.wait()

            print("{} : 此水幾時休 ".format(self.name))
            self.cond.notify()
            self.cond.wait()

            print("{} : 只願君心似我心 ".format(self.name))
            self.cond.notify()
            self.cond.wait()



if __name__ == "__main__":
    from concurrent import futures
    cond = threading.Condition()
    xiaoai = XiaoAi(cond)
    tianmao = TianMao(cond)

    # 啓動順序很重要
    # 在調用with cond以後才能調用wait或者notify方法
    # condition有兩層鎖, 一把底層鎖會在線程調用了wait方法的時候釋放,
    # 上面的鎖會在每次調用wait的時候分配一把並放入到cond的等待隊列中,
    # 等到notify方法的喚醒
    xiaoai.start()
    tianmao.start()
相關文章
相關標籤/搜索