多线程使用

多线程

# 1. 导入模块
import threading
# 创建线程(让每一个线程去执行这个函数(有参数就给参数))
t = threading.Thread(target=函数名,args=())
# 线程开始工作
t.start()

多进程

# 导包
import multiprocessing

# 创建进程(进程创建之后,在进程中还会创建一个线程)
t = multiprocessing.Process(target=函数名,args=())

#启动线程
t.start()

代码要放在  中
if __name__ == '__main__':
在linux系统中没有这句不会报错
# 因为linux系统 支持模式fork;  win :spawn; mac支持:fork和spawn(但是在python3.8以后默认设置为spawn)
python机制导致的问题
# 在最开头加一句  更改设置
multiprocessing.set_start_method('fork')

多进程开销比多线程大

GIL锁

CPython解释器中的一个全局解释器锁
# 让一个进程同一时刻只能由一个线程可以被cpu调用

# 想要利用计算机多核优势,让cpu同时处理一些任务,适合用多进程开发(资源开发大)

# 不需要利用多核优势,可以用多线程

计算密集型: 多进程  大量数据计算
io秘密集型:多线程  文件读写,网络数据传输(下载视频)

多线程开发

程序最开始运行的时候,它内部会先创建一个线程,而一个进程里面模式是有一个主线程

主线程会执行完所有代码,不结束(等待子线程)

常用方法

t.start()
# 当前线程准备就绪(等待CPU调度,具体时间由cpu决定)
t.join()
# 等待当前线程的任务执行完毕后,向下继续执行

就是
主线程遇到
t.join()
# 主线程等待,直到子线程结束,再向下执行


如果有多个子线程
t1.start() 			# t1开始执行
t1.join()			# t1运行完毕,再往下运行
t2.start()			#  t2开始执行
t2.join()			# t2运行完毕,再往下运行

# 案例
	两个线程同步做。cpu在执行任务时,分片机制,cpu会分片的执行,t1执行
	几个步骤,t2执行几个步骤,cpu在两个线程中来回切。
t1.start()
t2.start()
t1.join()
t2.join()
t.setDaemon(布尔值)
# 守护线程(必须放在start之前)

t.setDaemon(True)	设置守护线程。主线程完毕后,子线程也会自动关闭。
t.setDaemon(False)	设置非守护线程。主线程等待子线程,子线程执行完毕后,主线程才关闭(默认)。
t.setName() getName()
t.setName()  
# 案例
t = threading.Thread(target=XXX,args=())
t.setName('线程1')
t.start()
	setName要放在start之前。不然设置不上


getName()

name = threading.current_thread().getName()
自定义线程类
import threading

class MyThread(threading.Thread):
    def run(self):
     	print('执行此此线程',self._args)   

t = MyThread(args=(100,))
t.start()

线程安全

创建锁

lock_object = threading.RLock()

加锁:

lock_object.acquire()

释放锁

lock_object.release()

# 他们必须用同一把锁谁是第一个到的,谁就把锁申请到了,然后加锁,继续往下执行。而没有
申请到这把锁的就要等待。等待加锁的那个释放了。
# 可以基于上下文管理,内部自动执行
# acquire 和release
# 就和文件操作类似
with lock_object:
    global num
    for i in range(100):
        num+=1
print(num)

线程锁

一次锁一次解。lock效率高

Lock 同步锁

lock 不支持锁的嵌套
# 锁了一次解开,然后再锁
lock_object.acquire()
...
lock_object.release()

lock_object.acquire()
...
lock_object.release()


# 死锁(锁一次没解锁又锁)
lock_object.acquire()
...
lock_object.acquire()

RLock 递归锁

# 开发中rlock用的还是比较多

import threading
lock = threading.RLock()
# A开发了一个函数,可以被其他人调用(有锁)
def func():
    with lock:
        pass
# B开发函数需要加锁,还要调用func()
def process()
	with lock:
        print('...')
        func()		# ........此时就会出现多次锁,只有rlock支持
        print('...')

死锁

由于竞争资源或者由于彼此通信而造成的一种阻塞现象

lock_object.acquire()
lock_object.acquire()
...
lock_object.release()
lock_object.release()
import threading 
import time

lock1 = threading.Lock()
lock2 = threading.Lock()

def task1():
    lock1.acquire()		# 获取第一把锁
    time.sleep(1)
    lock2.acquire()		
    print(11)
    lock2.release()
    print(111)
    lock1.release()
    print(1111)

def task2():
    lock2.acquire()		#获取第二把锁
    time.sleep(1)
    lock1.acquire()
    print(22)
    lock1.release()
    print(222)
    lock2.release()
    print(2222)

# 如果两个人都不释放锁,就会死锁

线程池

线程不是开的越多越好。开得多可能会导致系统的性能更低了

 # 使用线程池  导包
from concurrent.futures import ThreadPoolExecutor

# 创建了一个线程池,最多维护100个线程
pool = ThreadPoolExecutor(100)
# 把一个任务交给线程池,让他安排线程帮助取执行
# 线程池中如果有空闲线程,则分配一个线程去执行,执行完毕后再将线程交给线程池

# 如果没有空闲线程,就等待
pool.submit(函数名,参数1,...)
等待线程池的任务执行完毕
pool.shutdown(True)# 等待线程池内的任务执行完毕,再继续执行# 类似于join
add_done_callback(done)

可以做分工,task专门下载,done专门将下载的东西写入本地

future = pool.submit(task,url)
future.add_done_callback(done)
# 当线程执行完毕,再执行一下done函数

# 线程池先去安排一个线程去执行这个任务(task),任务执行完,再执行一下done
练习题

单例模式

面向对象+多线程相关的面试题

   class Singleton:
    instance = None
    lock = threading.RLock()
    
    def __init__(self,name):
        self.name = name
    def __new__(cls,*args,**kwargs):
        # 返回空对象
        if cls.instance:
            return cls.instance
        
        with cls.lock:
            if cls.instance:
                return cls.instance
            cls.instance = object.__new__(cls)
            return cls.instance

总结

1. 简述进程线程的区别以及应用场景

1. 线程是计算机中可以被cpu调度的最小单元(真正在工作)2. 进程是计算机资源分配的最小单元
2. (进程为线程提供资源)3. 一个进程可以有多个线程,同一个进程中的线程可以共享此进程中的
3. 资源# 由于GIL锁的存在,控制一个进程中同一时刻只有一个线程可以被CPU调度		
4. 计算密集型:适合多进程		
5. IO密集型:适合多线程

2.GIL锁

GIL锁是cPython解释器特有的一个全局解释器锁。控制一个进程中同一时刻只有一个线程可以
被CPU调度同时像列表。字典等常见对象的线程数据安全,也得益于GIL

3.手写单例模式


4. 判断

t = threading.Thread(target = wait)

# 默认值。主线程不会终止,等待子线程
t.setDaemon(False)

# 主线程结束,子线程就结束,不会等待
t.setDaemon(True)

# 等子线程完了,主线程才能继续
t.join()
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值