Python之佳

python多线程

2019-01-09  本文已影响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()

```

上一篇下一篇

猜你喜欢

热点阅读