线程
1 .简述进程和线程的区别以及应用场景
#线程,是计算机中可以被cpu调度的最小单元(真正在工作)
#进程,是计算机资源分配的最小单元
#计算密集型,适用多进程开发
#IO密集型,适用多线程开发
2.GIL锁
全局解释器锁,控制一个进程中同一时刻只有一个线程可以被cpu调度
3 线程的常用方法
3-1 t.start()
当前线程准备就绪 (等待cpu调度,具体时间是由cpu来决定)
import threading
def funs(name):
print(name)
for li in range(10):
t1=threading.Thred(target=funs,args=(li,))
t1.start()
3-2 t.join()
等待当前线程的任务执行完毕后再向下继续执行。
import threading
t=threading.Thred(target=fun)
t.start() #子线程被调用
t.join() #主线程等待
3-3 t.setDaemon(布尔值)
守护线程(必须放在start之前)
import threading
t=threading.Thread(target=fun,args=('tian',li))
# True 设置守护线程,主线程执行完毕后,子线程也自动关闭
t.setDaemon(True)
# 设置为非守护线程,主线程等待子线程,子线程执行完毕后,主线程才结束(默认)
t.setDaemon(False)
t.start()
3-4 线程名称的设置和获取
import threading
def task(arg):
#获取当前执行此代码的线程
name = threading.current_thread().getName()
print(name)
for i in range(10):
t=threading.Thread(target=task,args=(11))
#设置线程名
t.setName('hello{}'.format(i))
t.start()
3-5 自定义线程类
直接将线程需要做的事写到run方法中。
# 不使用初始化的方式传参
import threading
class MyThread(threading.Thread):
def run(self, thread_id, name):
self.thread_id = thread_id
self.name = name
print(f"Starting {self.name}")
# 线程具体逻辑
print(f"Exiting {self.name}")
if __name__ == '__main__':
# 创建新线程并传递参数
thread1 = MyThread(target=MyThread.run, args=(1, "Thread-1"))
thread2 = MyThread(target=MyThread.run, args=(2, "Thread-2"))
# 开启新线程
thread1.start()
thread2.start()
# 等待所有线程完成
thread1.join()
thread2.join()
print("Exiting main thread")
#使用初始化方式传参
import threading
class MyThread(threading.Thread):
def __init__(self,age,name):
self.age=age
self.name=name
def run(self):
print('执行此线程',self.age,self.name)
t=mythread(18,'tian')
t.start()
4 线程安全
4-1 加锁
import threading
#创建锁
lock_object = threading.RLock()
loop= 100000
number =0
def add(count):
#加锁
lock_object.acquire()
global number
for i in range(count):
number+=1
#解锁
lock_object.release()
def sub(count):
#加锁
lock_object.acquire()
global number
for i in range(count):
number-=1
#解锁
lock_object.release()
# add()和sub() 使用了同一把锁,这样才能让线程安全,
5 线程锁
在程序中如果想要自己手动加锁,一般有两种:Lock和RLock
在使用线程锁时,应该保证获取锁和释放锁的顺序正确,否则可能会出现死锁的情况。因此,建议使用 with lock:
的上下文管理器来自动获取和释放线程锁
5-1 Lock 同步锁
import threading
lock_object=threading.Lock()
def task():
print()
lock_object.acquire() #上锁
pass
lock_object.release() #解锁 线程出去,其他线程就可以进行并执行了
5-2 RLock 递归锁
import threading
lock=threading.RLock()
def func():
with lock:
pass
def run():
print()
func()
def process():
with lock:
print()
lock() #这里已经加了两个锁,使用Lock()会锁死,使用RLock()不会锁死
t1=threading.Thread(target=run)
t2=threading.Thread(target=process)
t1.start()
t2.start()
t1.join()
t2.join()
6 线程池
1
from concurrent.futures import ThreadPoolExecutor
#创建了100个线程
pool= ThreadPoolExecutor(100)
def task(url):
pass
url_list=['www.xxx-{}.com'.format(i) for i in range(300)]
for url in url_list:
pool.submit(task,url)
2 等待执行 shutdown(True) 与join()类似
from concurrent.futures import ThreadPoolExecutor
pool= ThreadPoolExecutor(100)
def task(url):
pass
url_list=['www.xxx-{}.com'.format(i) for i in range(300)]
for url in url_list:
pool.submit(task,url)
print("执行中")
pool.shutdown(True) #等待线程池中的任务执行完毕后,在继续执行
print("继续往下走")
分工模式
import concurrent.futures as poo
import random
pool=poo.ThreadPoolExecutor(10)
def get_request(url):
pass
return random.randint(1,100)
def down(res):
# res=response.result()
res=res.result()
print(res)
for li in range(10):
wood=pool.submit(get_request,li)
wood.add_done_callback(down)
分工模式 (闭包使用)
import concurrent.futures as poo
import random
pool=poo.ThreadPoolExecutor(10)
def get_request(url):
pass
return random.randint(1,100)
def fun(li):
def down(res):
# res=response.result()
res=res.result()
print(f"{li}次{res}")
return down
# 说明 在分工模式中,down(res)只能接受一个形参,如果想接受多个形参,要使用到闭包,在闭包中,可以传入多个参数
for li in range(10):
wood=pool.submit(get_request,li)
wood.add_done_callback(fun(li))
4 线程池加锁
需要注意的是,在使用线程池时,推荐使用 with concurrent.futures.ThreadPoolExecutor()
上下文管理器,以确保线程池中的工作线程能够正确地启动和关闭。同时,在线程池中使用线程锁时,需要保证获取锁和释放锁的顺序正确,否则可能会出现死锁的情况。
import concurrent.futures
import threading
count = 0 # 共享资源
lock = threading.Lock() # 创建线程锁对象
def worker(id):
global count, lock
for i in range(100000):
with lock:
count += 1
print(f"Worker {id} finished")
if __name__ == '__main__':
# 创建线程池并提交任务
with concurrent.futures.ThreadPoolExecutor(max_workers=2) as executor:
executor.submit(worker, 1)
executor.submit(worker, 2)
print(f"Final count: {count}")
# Linux系统fork,win:spawn mac:fork和spawn (python默认 spawn)
#所以 在win下,运行进程的程序不在“if __name__ == '__main__': ”这里面,这会报错, 解决方法
multiprocessing.set_start_method('fork')
Executor(max_workers=2) as executor:
executor.submit(worker, 1)
executor.submit(worker, 2)
print(f"Final count: {count}")