线程(thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。
基本使用
- 导包
- import threading
- 实例化 ,传入函数名,参数。参数为元组形式传递
- t = threading.Thread(target=func, args=(arg1, ))
- 开启线程 ,不能使用run方法,否则就只是普通的调用函数
- t.start()
- 线程等待,等待子线程结束、主线程再继续执行
- t.join()
- 守护线,主线程不会等待子线程结束
- t.setdaemon(True)
- 查看线程数量
- t.enumerate()
继承方式创建线程
import threading
class Producer(threading.Thread):
def __init__(self, num):
super().__init__()
self.num = num
def run(self):
for i in range(self.num):
print(f"producer {i}\n")
class Consumer(threading.Thread):
def __init__(self, num):
super().__init__()
self.num = num
def run(self):
for i in range(self.num):
print(f"consumer {i}\n")
if __name__ == '__main__':
p = Producer(5)
c = Consumer(3)
p.start()
c.start()
p.join()
c.join()
- 参数传递需要初始化基类
- 主线程不会等待子线程结束后再结束
- 子线程在运行结束后会销毁
- 不使用run方法开启线程,如果使用run方式只是普通的调用函数
线程间通信
- 线程之间可以共享全局变量,如下代码,如果数据正确最终num应该为2000001,但显然结果并不是。
import threading
num = 1
def func1():
global num
for i in range(1000000):
num += 1
def func2():
global num
for i in range(1000000):
num += 1
if __name__ == '__main__':
t = threading.Thread(target=func1)
t1 = threading.Thread(target=func2)
t.start()
t1.start()
t.join()
t1.join()
print("num",num)
![image.png](https://cdn.nlark.com/yuque/0/2020/png/1325594/1594280622245-b85d6367-0a27-4505-b96c-ef48113a8541.png#align=left&display=inline&height=68&margin=%5Bobject%20Object%5D&name=image.png&originHeight=144&originWidth=1207&size=42080&status=done&style=none&width=573)
- 多线程对同一数据进行写操作时会造成紊乱,所以需要线程需要进行同步
线程锁
- 某个线程要更改共享数据时,先将其锁定,此时资源的状态为”锁定”,其他线程不能改变,只到该线程释放资源,将资源的状态变成”非锁定”,其他的线程才能再次锁定该资源。
- 互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性。
- 方法
- mutex = threading.Lock()
- mutex.acquire()
- mutex.release()
import threading
num = 1
def func1(lock):
global num
for i in range(1000000):
lock.acquire()
num += 1
lock.release()
def func2(lock):
global num
for i in range(1000000):
lock.acquire()
num += 1
lock.release()
if __name__ == '__main__':
# 创建线程锁
lock = threading.Lock()
t = threading.Thread(target=func1, args=(lock,))
t1 = threading.Thread(target=func2, args=(lock,))
t.start()
t1.start()
t.join()
t1.join()
print("num",num)
- 可见,对数据相同的数据操作上锁之后,这次的结果就是正确的了,保证了数据的准确性
队列(FIFO)
- from queue import Queue 导入
- q = Queue() 实例化
- 方法
- 本质区别
- 线程是任务调度和执行的最小单位
- 进程是程序资源调度单位,包含线程
- 包含关系
- 线程属于进程的一部分
- 进程可以拥有多个线程
- 内存开销
- 复制一份代码、资源创建子进程。进程之间拥有相互独立的内存控件,切换开销较大
- 线程共享资源,并且使用的是进程里的资源,切换开销较小
- 适用条件
- 线程适用于IO密集型操作,如爬虫
- 进程适用于计算密集型操作,如数据建模