线程★★★★★

本文详细介绍了Python中的线程概念,包括线程的并发优势、资源共享、类定义线程、线程函数、守护线程、锁的概念及使用、信号量Semaphore、事件Event以及线程队列Queue、LifoQueue和PriorityQueue的应用。通过实例模拟连接远程数据库,强调了在多线程中保证数据安全和同步的重要性,并总结了进程与线程的区别和联系。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

### 线程

进程: 资源分配最小单位
线程: cpu执行程序的最小单位 
一个进程必须有一个线程,一个线程可以包含多个线程

##一份进程资源中可以包含多个线程

from threading import Thread  ==>引入线程(属于一个类)
from multiprocessing import Process  ==>引入进程(属于一个类)
import os
def func(num):
	print('当前线程{},所归属的进程id号{}'.format(os.getpid(),num))
if __name__=="__main__":
	for i in range(10):
		# 异步创建10个子线程
		t = Thread(target=func,args=(i,))
		t.start()
	主线程执行任务
	print(os.getpid())

## 并发多线程和多进程 , 谁的速度更快? 多线程!

import time
def func(num):
	print('当前线程{},所归属的进程id号{}'.format(os.getpid(),num))

if __name__=='__main__':

	多线程测试;
	#记录开始时间
	starttime=time.time()
	lst=[]
	for i in range(1000):
		t=Thread(target=func,args=(i,))
		t.start()
		lst.append(t)
		# 等到所有子线程执行完毕
	for i in lst:
		i.join()
	# 记录结束时间
	endtime=time.time()
	print("多线程执行时间:",endtime-starttime)
	

	多进程测试:
	#记录开始时间
	starttime=time.time()
	lst=[]
	for i in range(1000):
		p=Process(target=func,args=(i,))
		p.start()
		lst.append(p)
		# 等到所有子线程执行完毕
	for i in lst:
		i.join()
	# 记录结束时间
	endtime=time.time()
	print("多进程执行时间:",endtime-starttime)

##多线程 共享同一份进程资源

num =1000
def func():
	global num
	num -=1
if __name__=="__main__":
for i in range(1000):
	t=Thread(target=func)
	t.start()

print(num)

##用类定义线程

from threading import Thread
import time
import os

class MyThread(Thread):  ===>必须继承父类
	def __init__(self,name):  ==>如果传参数,必须调用父类的构造方法
		# 手动调用父类的构造方法
		super().__init__()  ==>super()只调用父类
		self.name = name

	def run(self):  ===>run方法不可以乱写还有(handle)
				time.sleep(1)
		print("当前进程正在执行runing ... " , self.name)
	
if __name__ == "__main__":
	t = MyThread("机器今天会再次爆炸么?")
	t.start()
	print("主线程执行结束 ... ")

##线程相关的函数

线程.is_alive()    检测线程是否仍然存在
线程.setName()     设置线程名字
线程.getName()     获取线程名字
1.currentThread().ident 查看线程id号 
2.enumerate()        返回目前正在运行的线程列表
3.activeCount()      返回目前正在运行的线程数量

例如:
def func():
	time.sleep(1)
if __name__ == "__main__":
	t = Thread(target=func)
	t.start()
	
	# print(t , type(t)) ===>对象
	print(t.is_alive()) # False  ==>判断子线程是否还活着
	print(t.getName())  ==>获取名字
	t.setName("xboyww")  ===>修改子线程名字
	print(t.getName())  ===>将修改的名字再去获取


1.currentThread().ident 查看线程id号 
from threading import currentThread  ==>需要引入(currentThread这个一个类)
例如:
def func():
	print("子线程id",currentThread().ident  , os.getpid())

if __name__ == "__main__":
	Thread(target=func).start()
	print("主线程id",currentThread().ident , os.getpid())
	


2.enumerate()        返回目前正在运行的线程列表(死了的线程不展示)
from threading import enumerate
from threading import activeCount # (了解)
例如:
def func():
	print("子线程id",currentThread().ident  , os.getpid())
	time.sleep(0.5)

if __name__ == "__main__":
	for i in range(10):
		Thread(target=func).start()
	lst = enumerate()
	# 子线程10 + 主线程1个 = 11
	# print(lst ,len(lst))


# 3.activeCount()      返回目前正在运行的线程数量
print( activeCount() )  ===>打印对象会触发__str__方法

##守护线程 : 等待所有线程全部执行完毕之后,在自己终止,守护的是所有线程

from threading import Thread
import time
def func1():
	while True:
		time.sleep(0.5)
		print("我是func1")

def func2():	
	print("我是func2 start ... ")
	time.sleep(3)
	print("我是func2 end ... ")

t1 = Thread(target=func1)
t2 = Thread(target=func2)

在start调用之前,设置守护线程
t1.setDaemon(True)
t1.start()
t2.start()
print("主线程执行结束 ... ")

##Lock 保证线程数据安全

from threading import Lock,Thread
import time
例如:
n = 0  ===>多个线程可以共用一个进程里面的数据

def func1(lock):
	global n
	
	for i in range(1000000):
	# 方法一
	lock.acquire()
		n -= 1
	lock.release()

def func2(lock):
	global n
	# 方法二 	# with 自动完成上锁+解锁	
	with lock:  ==>加在外面最好
		for i in range(1000000):	
			n += 1

if __name__ == "__main__":
	lst = []
	lock = Lock()

	time1 = time.time()
	for i in range(10):
		t1 = Thread(target=func1,args=(lock,))
		t2 = Thread(target=func2,args=(lock,))
		t1.start()
		t2.start()
		lst.append(t1)
		lst.append(t2)
	
	# 等待所有的子线程执行结束之后, 在打印数据
	for i in lst:
		i.join()
	

	time2 = time.time()
	print("主线程执行结束..." , n ,time2 - time1)

##信号量 Semaphore (线程)

from threading import Semaphore,Thread
import time


def func(i,sm):
	with sm:	
		print(i)
		time.sleep(3)
	

if __name__ == "__main__":
	sm = Semaphore(5)
	for i in range(20):
		Thread(target=func,args=(i,sm)).start()



总结:
再创建线程的时候是异步创建
在执行任务的时候,因为Semaphore加了锁,所以线程之间变成了同步;

##事件 Event

from threading import Event,Thread
import random,time

e = Event()

# wait   动态加阻塞
# clear  将阻塞事件的值改成False
# set    将阻塞事件的值改成True
# is_set 获取阻塞事件的值
(1) 基本语法

e = Event()
print(e.is_set())
e.set()
e.clear()
e.wait(3)
print("程序在运行 ... ")

模拟连接远程数据库(阿里)

"""连接三次数据库,如果都不成功,直接抛出异常"""
def check(e): ==>负责执行检测任务

	# 模拟网络延迟
	time.sleep(random.randrange(1,6))

	# 开始检测链接合法性
	print("开始检测链接合法性")
	e.set()  ==>如果检测都合法,将阻塞态变成放行态

def connect(e):  ===>负责执行连接任务
	sign = False  ==.>设置一个状态
	for i in range(1,4):  ==>设置连接的次数(如果三次都不合法,执行if分支)
		# 设置最大等待时间 1
		e.wait(1)
		if e.is_set():
			print("数据库链接成功 ... ")
			sign = True
			break
		else:
			print("尝试链接数据%s次失败" % (i) )

	if sign == False:  ===>如果都不成功,抛出异常
		# 主动抛出异常
		raise TimeoutError	
	

e = Event()

线程1负责执行检测任务
Thread(target=check,args=(e,)).start()
线程2负责执行连接任务
Thread(target=connect,args=(e,)).start()

##线程队列

from queue import Queue

put 存
get 取
put_nowait 存,超出了队列长度,报错
get_nowait 取,没数据取不出来,报错

linux windows 线程中put_nowait,get_nowait都支持

Queue

"""先进先出,后进后出"""
q = Queue()
q.put(1)
print(q.get())
取不出来,阻塞
print(q.get())
print(q.get_nowait())==>报错
q2 = Queue(3)
q2.put(11)
q2.put(22)
q2.put(33)
放不进去了,阻塞
q2.put(44)
q2.put_nowait(44)

LifoQueue 先进后出,后进先出(按照栈的特点设计)

from queue import LifoQueue
lq = LifoQueue(3)
lq.put(11)
lq.put(22)
lq.put(33)
print(lq.put_nowait(444))

print(lq.get())
print(lq.get())
print(lq.get())

PriorityQueue 按照优先级顺序排序 (默认从小到大排序)

from queue import PriorityQueue

如果都是数字,默认从小到大排序
pq = PriorityQueue()
pq.put(13)
pq.put(3)
pq.put(20)
print(pq.get())
print(pq.get())
print(pq.get())

如果都是字符串
"""如果是字符串,按照ascii编码排序"""
pq1 = PriorityQueue()
pq1.put("chinese")
pq1.put("america")
pq1.put("latinos")
pq1.put("blackman")

print(pq1.get())
print(pq1.get())
print(pq1.get())
print(pq1.get())
要么全是数字,要么全是字符串,不能混合 error

pq2 = PriorityQueue()
pq2.put(13)
pq2.put("aaa")
pq2.put("拟稿")


pq3 = PriorityQueue()
默认按照元组中的第一个元素排序
pq3.put( (20,"wangwen") )
pq3.put( (18,"wangzhen") )
pq3.put( (30,"weiyilin") )
pq3.put( (40,"xiechen") )

print(pq3.get())
print(pq3.get())
print(pq3.get())
print(pq3.get())

总结(进程和线程):

不同点:
	1:
	进程状态可以共享(Lock,Event,Semaphore,通过soket),资源不共享.共享需要通过队列(Queue,JoinableQueue,Manager) ,进程可以并行和并发
	线程状态和资源是共享的(共享同一份进程),可以并发,不可并行.
	
	2:
	进程: 资源分配最小单位(划分资源)
	线程: cpu执行程序的最小单位
	
	3:
	线程的相关函数:
			线程(对象).is_alive()    	检测线程是否仍然存在
			线程(对象).setName(参数)     设置线程名字
			线程(对象).getName()     	获取线程名字
			lst = enumerate()       			返回目前正在运行的线程列表    引入from threading import enumerate
							
	




相同点:
	Lock (锁):
			进程锁:
					意义:同一时间上一把锁,会在自己内部给各个进程发出消息(状态)
					引入锁: from  muiltprocessing  import  Process,Lock
					创建一把锁(相当于定义,写在if下面) : lock = Lock()
					使用:
						   	上锁	lock.acquire()           			简写为:
									执行操做....							with  lock:
							解锁	lock.release()									执行操做....		

					例子:模拟12306 抢票软件
					
					
			线程锁:
					意义: 保证线程数据安全
					引入锁: from threading import Lock,Thread
					创建一把锁(相当于定义,写在if下面) : lock = Lock()
							使用:
						        上锁	lock.acquire()           		简写为:
										执行操做....							with  lock:
								解锁	lock.release()									执行操做....





	Event(事件):
			进程(阻塞事件):
					引入事件: from multiprocessing import Process,Event
					生成事件对象e(相当于定义,写在if下面) :e = Event() 
					含有的使用方法:
							e.wait()动态给程序加阻塞 ,一个程序当中是否加阻塞完全取决于该对象中的is_set() [默认返回值是False] 如果是True  不加阻塞如果是False 加阻塞 
							e.set()方法     将这个属性的值改成True
							e.clear()方法   将这个属性的值改成False
							e.is_set()方法  判断当前的属性是否为True  (默认上来是False)
					例子:模拟红绿灯效果
			
			
			线程(事件):
					引入事件:from  threading  import Thread ,Event
					生成事件对象e(相当于定义,写在if下面) :e = Event() 
					含有的使用方法:
							e.wait()动态给程序加阻塞 ,一个程序当中是否加阻塞完全取决于该对象中的is_set() [默认返回值是False] 如果是True  不加阻塞如果是False 加阻塞 
							e.set()方法     将这个属性的值改成True
							e.clear()方法   将这个属性的值改成False
							e.is_set()方法  判断当前的属性是否为True  (默认上来是False)
					例子:模拟连接远程数据库





			
	队列:
			进程:
					作用:可以让进程之间共享数据
					包含:Queue, JoinableQueue, Manager
							Queue:	
									引入:from multiprocessing import Process,Queue
									生成对象q(相当于定义,写在if下面) : q = Queue()
									可以限定队列的长度: q= Queue(3)===>最多塞3个值,超出了队列的长度,会发生阻塞
									特点: 先进先出,后进后出
									含有的使用方法:
												q.put(参数) 往队列中存值
												res=q.get() 从队列中取值  ==>队列里面没数据了,在调用get会发生阻塞
									例子: 生产者消费者模型
								
							JoinableQueue:
									引入: from multiprocessing import Process,JoinableQueue
									生成对象jq(相当于定义,写在if下面) : jq = JoinableQueue()
									含有的使用方法:
												jq.put(参数) 存储,通过task_done,会让队列计数器+1
												res=jq.get()  获取,通过task_done,让队列计数器-1
												jq.task_done() 队列计数减1
												jq.join()  阻塞,函数,会根据队列中的计数器来判定是阻塞还是放行,如果计数器变量是0,意味着放行,其他情况阻塞,task_done 配合 join 一起使用
									例子: 优化生产者消费者模型	
								
							Manager :
									引入:from multiprocessing import Process,Manager,Lock   ==>必须引入Lock(上锁)
									生成对象m(相当于定义,写在if下面):	m = Manager()
									在创建一个共享的字典                  or             创建一个共享的列表						
									data = m.dict( {"count":20000} )                    data = m.list([1,2,3])



			线程:
		        	包含:Queue,LifoQueue,PriorityQueue
							Queue:
									引入:from queue import Queue
									生成对象q(相当于定义,写在if下面):q = Queue()
									可以限定队列的长度: q= Queue(3)===>最多塞3个值,超出了队列的长度,会发生阻塞
									特点: 先进先出,后进后出
									含有的使用方法:
												q.put(参数)  往队列中存值
												res=q.get()  从队列中取值  ==>队列里面没值会阻塞
												
							LifoQueue:
									引入:from queue import LifoQueue
									生成对象lq(相当于定义,写在if下面):lq = LifoQueue()
									可以限定队列的长度: lq= Queue(3)===>最多塞3个值,超出了队列的长度,会发生阻塞
									特点:先进后出,后进先出
									含有的使用方法:
												lq.put(参数)  往队列中存值
												res=lq.get()  从队列中取值  ==>队列里面没值会阻塞
												
							PriorityQueue:
									意义: 按照优先级顺序排序 (默认从小到大排序)
									引入:from queue import PriorityQueue
									生成对象pq(相当于定义,写在if下面):pq = PriorityQueue()
									含有的使用方法:
												pq.put(参数)  往队列中存值
												res=pq.get()  从队列中取值  ==>队列里面没值会阻塞
														1:如果都是数字,默认从小到大排序
																pq.put(12)
																pq.put(23)
																res=pq.get()
														2:如果是字符串,按照ascii编码排序,小的先排(要么全是数字塞,要么全是字符串塞,不能混合塞)
																pq.put('china')
																pq.put('america')
																res=pq.get()
														3:默认按照元组中的第一个元素排序(如果混合塞,要塞成元组形势)
																pq.put( (30,"weiyilin") )
																pq.put( (40,"xiechen") )
																res=pq.get()



					


	Semaphore(锁):
			进程:
					意义:本质上就是锁,可以控制上锁的数量
					引入:from multiprocessing import Semaphore,Process
					生成对象sm(相当于定义,写在if下面):sm= Semaphore()
					可以限定锁的数量: sm= Semaphore(3)===>最多塞3个值,超出了会发生阻塞
					使用:
					   	上锁	sm.acquire()           			简写为:
									执行操做....							with  sm:
							解锁	sm.release()									执行操做....

			线程:
					意义:本质上就是锁,可以控制上锁的数量
					引入:from threading import Semaphore,Thread
					生成对象sm(相当于定义,写在if下面):sm= Semaphore()
					可以限定锁的数量: sm= Semaphore(3)===>最多塞3个值,超出了会发生阻塞
					使用:
					   	上锁	sm.acquire()           			简写为:
									执行操做....							with  sm:
							解锁	sm.release()									执行操做....

			
			



	守护:
			守护进程:
					作用:守护进程守护的是主进程,如果主进程执行结束了,意味着守护进程的寿命立刻终止.立刻杀死
					语法:
						进程(对象).daemon = True  ==>设置当前进程为守护进程必须写在start()调用进程之前进行设置
						
			守护线程:
					作用: 等待所有线程全部执行完毕之后,在自己终止,守护的是所有线程
					语法:
						线程(对象).setDaemon(True)  ==>在start调用之前,设置守护线程 ==>注意D大写





	id:
			进程id:
					引入:import os
							当前进程的父进程id(父进程的进程号)
							res= os.getppid()
							当前进程的子进程id(子进程的进程号)
							res= os.getppid()
							
			线程id:
					引入:from threading import currentThread 
							查看线程id号
							currentThread().ident 






	创建:
			创建进程:
					格式为:
							引入:from multiprocessing import Process
								if __name__ == "__main__": 
								p = Process(target=func,args=(n,))
								 p.start()
								
								
			创建线程:
					格式为:
							引入:from threading import Thread
								if __name__ == "__main__": 
								t = Thread(target=func,args=(i,))
								t.start()

另外:
1:进程和线程上锁都会从异步变成同步,有先有后,如果必须要同步执行操作(有先有后),将执行操作的内容前后进行上锁(上锁和解锁是一对)
2:一个进程必须有一个线程,一个线程可以包含多个线程
3:使用进程和线程(可以不引)的时候,要引入 if __name __ ==’__main __’:
4;在定义锁后,函数在使用锁时,会把锁返回的对象当做参数传进去
5:并发多线程和多进程 , 多线程的速度更快!
6:线程锁一般加在外面,进程锁一般是修改哪里加在哪里
7:当创建进程或者线程的时候是异步的.当执行某个方法时候,各个进程或者线程之间是同步的
8:在定义事件后,函数在使用事件时,会把事件返回的对象当做参数传进去
9:进程相当于一块地,线程是地里面干活的工人(工人可以是多个)
10:如果wait中有参数.代表是最多阻塞几秒,等待几秒后就开始执行,如果不加参数,会一直阻塞(除非将False改为True)
11:在使用Queue时,为了等待子进程把数据塞到队列中,在获取,要加一个join
12:在定义进程Queue和JoinableQueue后,函数在使用Queue和JoinableQueue时,会把Queue和JoinableQueue返回的对象当做参数传进去
13;在定义Manager后,然后在创建一个共享字典或者列表,函数在使用Manager时,会把创建共享字典或者共享列表返回的数据当做参数传进去
14:在使用Manage时候,必须要使用锁和join.
15:在进程的queue 或者jionablequeue共享数据是一个进程将数据传到另外一个进程中,是属于获取或者是储存,manager共享数据是进程修改共享的数据
16:在定义线程Queue和LifoQueue,PriorityQueue后,函数在使用Queue和Queue和LifoQueue,PriorityQueue时,会把Queue和Queue和LifoQueue,PriorityQueue返回的对象当做参数传进去
17:每一个进程必须有一个主线程,一个主线程可以包含多个子线程
18:并发(一个CPU执行多个)多线程和多进程 , 多线程速度更快(线程不需要开辟空间,进行要开辟空间)
19:线程和进程都是异步并发的程序(一个CPU执行多个),有阻塞会跳过,一个进程里面的线程也会创建多个子线程.
20:创建的多线程或者多进程返回的都是对象
21;多个线程在一个进程里面公用一个进程号(进程号id相同),但每个线程都有自己的id
22:线程的enumerate() 返回目前正在运行的线程列表,字典推导式中的enumerate是枚举,和 zip (拉链) 是一对 ,用来做字典推导式的,返回迭代器
23: 计算数据(多线程或者多进程)的时候在同步情况下才会准确,所以要加锁,进程和线程一样.因为在异步的情况下,可能在第一个程序算的时候还没有把结果放回去,第二个程序就开始算了.可能会导致重复.所以针对于异步修改同一份数据的时候,一定要加锁,(例如抢票,manager修改数据,线程的修改数据)
24:id要重新引入 ,lock等直接在引入进程(线程)后面即可,并且lock等要定义,他们所返回值会作为一个参数
25:semaphore可以控制上锁的数量(最多线程或者进程几个),线程和进程使用都一样(唱吧),lock只控制一个
26:进程和线程都是异步创建和同步执行(需要上锁)
27:python中的线程可以并发,但是不能并行(同一个进程下的多个线程不能分开被多个cpu同时执行),因为GIL锁.进程可以并发和并行
28:全局解释器锁(GIL)同一时间,一个进程下的多个线程只能被一个CPU执行,不能实现线程的并行操作. 解决方法:1:用多进程间接实现线程的并行.2:更换解释器(Pypy)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值