python多线程

作者: 派派森森 | 来源:发表于2019-01-09 13:06 被阅读0次

    ### GIL

    global interpreter lock(cpython)

    同一时刻只有一个线程运行在一个cpu上执行字节码(无法将多个线程映射到多个cpu上)

    ```python

    import dis

    def add(a):

        a = a + 1

        return a

    print(dis.dis(add))

    ```

    #### GIL在某些情况下会释放

    每次的结果都不一样 线程之间的安全问题

    GIL会根据执行的直接码行数或者时间片释放GIL

    遇到IO操作时主动释放

    ```python

    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操作为主的编程来说,多进程和多先产出的性能差别不大,甚至多线程比多进程的性能还高,因为多线程编程更加轻量级。

    #### 简单的线程

    ```python

    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))

    ```

    #### 重载线程实现多线程

    ```python

    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))

    ```

    ### 多线程之间的通信

    使用queue

    ```python

    # 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))

    ```

    ### 线程的同步问题

    在多线程编程中必须要面对的问题

    #### 无锁不安全的原因

    ```python

    # 没有锁

    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的资源))

    ```python

    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)

    ```

    #### 相互等待(资源竞争)

    ```python

    """

    A(a、b)

    acquire (a)

    acquire (b)

    B(a、b)

    acquire (b)

    acquire (a)

    # 解决办法

    B(a、b)

    acquire (a)

    acquire (b)

    """

    ```

    #### 可重入锁(Rlock)

    ```python

    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)

    用于复杂的线程间同步

    ```python

    # 没有条件锁 不能实现对话

    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()

    ```

    ```python

    # 条件锁

    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()

    ```

    相关文章

      网友评论

        本文标题:python多线程

        本文链接:https://www.haomeiwen.com/subject/ojnwrqtx.html