摘要:对于操作为主的编程来说,多进程和多先产出的性能差别不大,甚至多线程比多进程的性能还高,因为多线程编程更加轻量级。
GIL
global interpreter lock(cpython)
同一时刻只有一个线程运行在一个cpu上执行字节码(无法将多个线程映射到多个cpu上)
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操作为主的编程来说,多进程和多先产出的性能差别不大,甚至多线程比多进程的性能还高,因为多线程编程更加轻量级。
简单的线程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))
多线程之间的通信
使用queue
# 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()
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/42970.html
摘要:进程可创建多个线程来执行同一程序的不同部分。就绪等待线程调度。运行线程正常运行阻塞暂停运行,解除阻塞后进入状态重新等待调度。消亡线程方法执行完毕返回或者异常终止。多线程多的情况下,依次执行各线程的方法,前头一个结束了才能执行后面一个。 浅谈Python多线程 作者简介: 姓名:黄志成(小黄)博客: 博客 线程 一.什么是线程? 操作系统原理相关的书,基本都会提到一句很经典的话: 进程...
摘要:多线程的理解多进程和多线程都可以执行多个任务,线程是进程的一部分。多线程创建在中,同样可以实现多线程,有两个标准模块和,不过我们主要使用更高级的模块。多线程的应用场景。 1、多线程的理解 多进程和多线程都可以执行多个任务,线程是进程的一部分。线程的特点是线程之间可以共享内存和变量,资源消耗少(不过在Unix环境中,多进程和多线程资源调度消耗差距不明显,Unix调度较快),缺点是线程之间...
摘要:其次,解释器的主循环,一个名为的函数,读取字节码并逐个执行其中的指令。所有线程都运行相同的代码,并以相同的方式定期从它们获取锁定。无论如何,其他线程无法并行运行。 概述 如今我也是使用Python写代码好多年了,但是我却很少关心GIL的内部机制,导致在写Python多线程程序的时候。今天我们就来看看CPython的源代码,探索一下GIL的源码,了解为什么Python里要存在这个GIL,...
摘要:上一篇文章进程专题完结篇多进程处理的一般建议下一篇文章线程专题多线程使用的必要性进程线程进程能够完成多任务,比如在一个电脑上可以运行多个软件。由于占用资源少,也使得多线程程序并发比较高。 上一篇文章:Python进程专题完结篇:多进程处理的一般建议下一篇文章:Python线程专题1:多线程使用的必要性 进程VS线程 进程:能够完成多任务,比如在一个电脑上可以运行多个软件。线程:也能够...
摘要:也提供多线程支持,而且中的线程并非是模拟出来的多线程,而是系统级别的标准库提供了两个模块和。同一个变量,线程则会互相共享。例如多个线程对银行中的某一个账户进行操作。但是实际情况是随意切换线程。说到的多线程编程,就会绕不过。 该文章参考了http://www.liaoxuefeng.com/wi... 廖雪峰的教程。 一个进程至少有一个线程。Python也提供多线程支持,而且Python...
摘要:多线程和锁作者博客进程和线程进程是执行中的计算机程序。线程包括开始执行顺序和结束三部分。的多进程相关模块模块是高级别的多线程模块。线程锁当多线程争夺锁时,允许第一个获得锁的线程进入临街区,并执行代码。 Python 多线程和锁 作者博客:http://zzir.cn/ 进程和线程 进程是执行中的计算机程序。每个进程都拥有自己的地址空间、内存、数据栈及其它的辅助数据。操作系统管理着所有的...
阅读 4299·2021-11-22 13:53
阅读 3947·2021-11-19 11:29
阅读 2012·2021-09-08 09:35
阅读 3629·2020-12-03 17:26
阅读 757·2019-08-29 16:06
阅读 2421·2019-08-26 13:50
阅读 1406·2019-08-23 18:32
阅读 2430·2019-08-23 18:12