2、线程
2.1 定义
默认情况下,程序启动只有一个线程,这个线程就是主线程,线程是CPU调度的基本单位
2.2 统计线程的个数
import threading
print('hello')
print('#####统计当前线程运行的个数#######')
print(threading.active_count())
展示:
hello
#####统计当前线程运行的个数#######
1
注意点:
在没有添加进程的情况下,我们依旧可以找到一个线程,这个线程是主线程
2.3 创建多线程
def __init__(self, group=None, target=None, name=None,
args=(), kwargs=None, *, daemon=None)
案例:
import threading
# print('hello')
# print('#####统计当前线程运行的个数#######')
# print(threading.active_count())
def calm():
for i in range(10):
print('第',i,'次冷静以下')
def smoke():
for j in range(10):
print('第',j,'只烟')
if __name__ == '__main__':
# calm()
# smoke()
print('当前进程的线程个数',threading.active_count())
# 创建多线程
thread_smoke = threading.Thread(target=smoke)
thread_calm = threading.Thread(target=calm)
print('当前进程的线程个数',threading.active_count())
# 守护主线程
# thread_smoke.setDaemon(True)
# thread_calm.setDaemon(True)
# 调用
thread_smoke.start()
thread_calm.start()
print('当前进程的线程个数',threading.active_count())
# 守护子线程
thread_calm.join()
thread_smoke.join()
print('当前进程的线程个数',threading.active_count())
print('主线程已经结束')
exit()
展示:
当前进程的线程个数 1
当前进程的线程个数 1
第 0 只烟
第 1 只烟
第 2 第只烟
第 当前进程的线程个数3 只烟
第 04 3
次冷静以下只烟
第
5 只烟第
第 1 次冷静以下
6 只烟第
第 7 只烟
2第 8 只烟
第 次冷静以下9 只烟
第 3 次冷静以下
第 4 次冷静以下
第 5 次冷静以下
第 6 次冷静以下
第 7 次冷静以下
第 8 次冷静以下
第 9 次冷静以下
当前进程的线程个数 1
主线程已经结束
2.4 多线程的注意点
1、线程的执行是无序的,因为是CPU进行调度的
2、守护主线程,主线程退出,那子线程直接销毁
import threading
import time
# print('hello')
# print('#######统计当前线程运行的个数######')
# print(threading.active_count())
def calm():
for i in range(10):
time.sleep(0.1)
print('第',i,'次冷静一下')
def smoke():
for j in range(10):
time.sleep(0.1)
print('第',j,'支烟')
if __name__ == '__main__':
# smoke()
# calm()
print('当前进程的线程个数',threading.active_count())
#创建多线程
thread_smoke = threading.Thread(target=smoke)
print('当前进程的线程个数', threading.active_count())
thread_calm = threading.Thread(target=calm)
#守护主线程
thread_smoke.setDaemon(True)
thread_calm.setDaemon(True)
#调用
thread_smoke.start()
thread_calm.start()
# time.sleep(0.1)
# thread_calm.join()
# thread_smoke.join()
print('当前进程的线程个数', threading.active_count())
print('主线程已经结束')
exit()
展示:
当前进程的线程个数 1
当前进程的线程个数 1
当前进程的线程个数 3
主线程已经结束
3、守护子线程,只有当子线程全部结束之后,主线程才结束
def calm():
for i in range(10):
time.sleep(0.1)
print('第',i,'次冷静一下')
def smoke():
for j in range(10):
time.sleep(0.1)
print('第',j,'支烟')
if __name__ == '__main__':
# smoke()
# calm()
print('当前进程的线程个数',threading.active_count())
#创建多线程
thread_smoke = threading.Thread(target=smoke)
print('当前进程的线程个数', threading.active_count())
thread_calm = threading.Thread(target=calm)
#调用
thread_smoke.start()
thread_calm.start()
# time.sleep(0.1)
thread_calm.join()
thread_smoke.join()
print('当前进程的线程个数', threading.active_count())
print('主线程已经结束')
exit()
展示:
当前进程的线程个数 1
当前进程的线程个数 1
第 0 支烟
第 0 次冷静一下
第 1 支烟
第 1 次冷静一下
第 2 支烟
第 2 次冷静一下
第 3 支烟
第 3 次冷静一下
第 4 次冷静一下
第 4 支烟
第 5 次冷静一下
第 5 支烟
第 6 支烟
第 6 次冷静一下
第 7 支烟
第 7 次冷静一下
第 8 次冷静一下
第 8 支烟
第 9 支烟
第 9 次冷静一下
当前进程的线程个数 1
主线程已经结束
4、子线程之间共享全局变量,就会造成资源的争抢问题
2.5 互斥锁的问题
问题:
因为线程之间共享全局变量,所以,在多线程几乎在同时运行的时候,几乎同时修改同一个全局变量的时候,就要进行控制
此时,需要互斥锁
当某个线程需要修改资源的时候,先将资源进行锁定,其他线程不能修改该线程
当线程修改完成之后,并且释放完互斥锁之后,其他的线程才可以使用
互斥锁保证在当前只有一个线程可以使用修改同一个资源
import threading
num = 0
def add_num1():
global num
for i in range(10000000):
# 上锁
lock_flag = lock_obj.acquire(True)
if lock_flag:
num += 1
# 释放锁
lock_obj.release()
print('子线程1已经完成,此时得到的num',num)
def add_num2():
global num
for i in range(10000000):
# 上锁
lock_flag = lock_obj.acquire(True)
if lock_flag:
num += 1
lock_obj.release()
print('子线程2已经完成,此时得到的num',num)
# 程序入口
if __name__ == '__main__':
lock_obj = threading.Lock()
thread1 = threading.Thread(target=add_num1)
thread2 = threading.Thread(target=add_num2)
thread1.start()
thread2.start()
展示:
子线程2已经完成,此时得到的num 19524417
子线程1已经完成,此时得到的num 20000000
注意点:
1、确保了关键代码只能由一个线程从头到尾完整的执行完成
2、阻止了多线程的并发操作,包含锁的代码其实还是单线程执行的,效率下降了;
如果当前的程序出现了多个锁,可能会出现死锁的问题
2.6 死锁的问题
避免死锁的方法:
1、超时释放(timeout)
2、设计的时候,避免死锁
2.7 线程安全
2.7.1 队列
from threading import Thread
from queue import Queue
from queue import LifoQueue
# python2的写法
# from Queue import Queue
# 队列
def writedata():
for i in range(20):
# 判断队列是否已经满了,没有满就向队列中填充
if not queue.full():
queue.put(i)
else:
break
def readdata():
for i in range(3):
# 判断队列是否为空,不为空就从队列中取出数据
if queue.qsize() != 0:
data = queue.get()
print(data)
else:
break
# def readdata1():
# for i in range(2):
# if queue.qsize() != 0:
# data = queue.get()
# print(data)
# else:
# break
if __name__ == '__main__':
# 创建一个队列对象
queue = Queue(5)
thread1 = Thread(target=writedata)
thread2 = Thread(target=readdata)
# thread3 = Thread(target=readdata1)
thread1.start()
thread2.start()
# thread3.start()
展示:
0
1
2
2.7.2 队栈
# 队栈
from threading import Thread
from queue import LifoQueue
import threading
def writedata1():
for i in range(200000):
# 上锁
lock_flag = lock_obj.acquire(True)
# 判断队栈是否已经满了,没有满就向队栈中填充数据
if not q.full():
q.put(i)
lock_obj.release()
else:
lock_obj.release()
break
def readdata1():
for i in range(3):
# 上锁
lock_flag = lock_obj.acquire(True)
# 判断队栈是否为空,不为空就从队栈中取出数据
if q.qsize() != 0:
data = q.get()
print(data)
lock_obj.release()
else:
lock_obj.release()
break
def readdata2():
for i in range(2):
# 上锁
lock_flag = lock_obj.acquire(True)
if q.qsize() != 0:
data = q.get()
print(data)
lock_obj.release()
else:
lock_obj.release()
break
if __name__ == '__main__':
lock_obj = threading.Lock()
# 创建一个队栈对象
q = LifoQueue(5000)
thread1 = Thread(target=writedata1)
thread2 = Thread(target=readdata1)
thread3 = Thread(target=readdata2)
thread1.start()
thread2.start()
thread3.start()
展示:
4461
4460
4459
5002
5001