python多线程

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

```

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,905评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,140评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,791评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,483评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,476评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,516评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,905评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,560评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,778评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,557评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,635评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,338评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,925评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,898评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,142评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,818评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,347评论 2 342

推荐阅读更多精彩内容

  • 一文读懂Python多线程 1、线程和进程 计算机的核心是CPU,它承担了所有的计算任务。它就像一座工厂,时刻在运...
    星丶雲阅读 1,442评论 0 4
  • 我们前面提到了进程是由若干线程组成的,一个进程至少有一个线程。多线程优点: 在一个进程中的多线程和主线程分享相同的...
    第八共同体阅读 512评论 0 0
  • Python 多线程 多线程类似于同时执行多个不同程序,多线程运行有如下优点: 使用线程可以把占据长时间的程序...
    今早上阅读 345评论 0 0
  • 环境 xubuntu anaconda pycharm python https://www.cnblogs.co...
    Ericoool阅读 1,890评论 0 0
  • Python 面向对象Python从设计之初就已经是一门面向对象的语言,正因为如此,在Python中创建一个类和对...
    顺毛阅读 4,205评论 4 16