1.一个进程中可以有多个线程,进程是指在内存中开辟出一块空间,线程主要做一些耗时操作。一个进程中的多个线程共享该进程的资源是指cpu调用该进程中的某一个线程,然后该线程可以用该进程的资源,并不是多个线程同时用该进程的资源。
2.线程:Threading
(1)状态:新建对象—start—就绪——cpu调用运行——结束,当sleep时,该线程从cpu中退出,此时称为阻塞状态,当sleep时间结束时,该线程回到就绪状态,等待cpu的调用执行。
(2)线程是可以共享全局变量的
(3)GIL 全局解释器锁
(4)线程同步:当共享数据时,需要给线程加锁,一个线程执行它的任务前,需要先获取一把锁(lock),当执行完任务,该线程就释放锁,然后下一个线程才能被cpu调用运行。缺点:速度慢
(5)python底层只要用线程默认加锁,当cpu运算量达到某值时锁会自动释放。
3.进程:计算密集型
线程:耗时操作:爬虫、读写(IO)
4.多线程同步
# 多线程
import threading
import random
import time
lock = threading.Lock()
list1 = [0] * 10
def task1():
# 获取线程锁,如果已经上锁,则等待锁的释放
lock.acquire() # 阻塞
for i in range(len(list1)):
list1[i] = 1
time.sleep(0.5)
lock.release()
def task2():
lock.acquire() # 阻塞
for i in range(len(list1)):
print('---->', list1[i])
time.sleep(0.5)
lock.release()
if __name__ == "__main__":
t1 = threading.Thread(target=task1)
t2 = threading.Thread(target=task2)
t2.start()
t1.start()
t2.join()
t1.join()
print(list1)
输出结果:
5.死锁:存在两把及以上的锁,两个线程间相互制约
# 死锁
from threading import Thread, Lock
import time
lockA = Lock()
lockB = Lock()
class MyThread(Thread):
def run(self): # start()
if lockA.acquire(): # 如果可以获取到锁则返回True
print('self.name' + '获取了A锁')
time.sleep(0.1)
if lockB.acquire():
print('self.name' + '又获取了B锁,原来还有A锁')
lockB.release()
lockA.release()
class MyThread1(Thread):
def run(self): # start()
if lockB.acquire(): # 如果可以获取到锁则返回True
print('self.name' + '获取了B锁')
time.sleep(0.1)
if lockA.acquire():
print('self.name' + '又获取了A锁,原来还有B锁')
lockA.release()
lockB.release()
if __name__ == "__main__":
t1 = MyThread()
t2 = MyThread1()
t1.start()
t2.start()
输出结果:,然后一直处于等待状态。
解决死锁的方式:(1)重构代码
(2)在lock.acquire()中添加参数timeout
6.两个线程间通信:生产者和消费者
python的queue模块中提供了同步的、线程安全的队列类,包括FIFO(先入先出)队列Queue,LIFO(后入先出)队列LifoQueue,和优先级队列PriorityQueue。这些队列都实现了锁原理(可以理解为原子操作,即要么不做,要么就做完),能够在多线程中直接使用。
注意:当两个线程同时就绪时,cpu选择调用哪个线程执行是随机的,没有顺序。
# 线程间通信
import queue
import threading
import time
import random
def produce(q):
i = 0
while i < 10:
num = random.randint(1, 100)
q.put("生产者产生数据:%d" % num)
print("生产者产生数据:%d" % num)
time.sleep(1)
i += 1
q.put(None)
# 完成任务
q.task_done()
def consume(q):
while True:
item = q.get()
if item is None:
break
print("消费者获取到:%s" % item)
time.sleep(4)
# 完成任务
q.task_done()
if __name__ == '__main__':
q = queue.Queue(10)
arr = []
# 创建生产者
th = threading.Thread(target=produce, args=(q,))
th.start()
# 创建消费者
tc = threading.Thread(target=consume, args=(q,))
tc.start()
th.join()
tc.join()
print("over!")
输出结果:
7.自定义线程:线程对象调用方法start(),然后start()方法会执行run()方法
class MyThread(Thread):
def __init__(self,name):
super().__init__()
self.name = name
def run(self):
任务
t = MyThread('name')
t.start()
8.数据共享:进程共享数据和线程共享数据区别:
进程是每个进程中都有一份
线程是共同用一个数据,就会导致数据安全性问题,然后就要用到GIL———>伪线程