视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
python多线程的详细介绍(代码示例)
2020-11-27 14:20:33 责编:小采
文档

本篇文章给大家带来的内容是关于python多线程的详细介绍(代码示例),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。

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

下载本文
显示全文
专题