目录
1、多线程的意义
-
是CPU调度的基本单位
2、多线程创建
-
直接创建
线程对象 = thread.Thread(group=Nore,targt=None,args=(),kwargs={},*,daemon=None)
import threading
import time
def thre(text):
for i in range(5):
print(text)
time.sleep(1)
if __name__ == '__main__':
thread1 = threading.Thread(target=thre, args=('线程01',))
thread2 = threading.Thread(target=thre, args=('线程02',))
thread1.start() # 启动线程1
thread2.start() # 启动线程2
第一次运行结果:
1 11 12 2 3 13 14 4 15 5 6 16 17 7 8 18 9 19
第二次运行结果:
1 11 12 2 3 13 14 4 15 5 6 16 17 7 18 8 9 19
两次运行结果明显不一样,即两个线程按照各自的顺序交叉进行。
-
通过继承创建
class myThread(threading.Thread) : #构造子类继承threading.Thread父类
def __init__(self, 参数,): #对父类init方法进行改写
super().__init__(self) #继承父类init方法
self.属性 = 参数
def run(self) : #对run函数重写
方法体
-
重写父类__init__方法
对子类进行传参的同时,继承父类__init__方法
-
重写父类run方法
把要执行的代码写到run函数里面,线程在创建后会直接运行run函数
import threading
import time
class myThread(threading.Thread): # 继承父类threading.Thread
def __init__(self, threadID, name, counter):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.counter = counter
def run(self): # 把要执行的代码写到run函数里面 线程在创建后会直接运行run函数
print("Starting " + self.name)
time.sleep(3)
print("Exiting " + self.name)
if __name__ == '__main__':
# 创建新线程
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)
# 开启线程
thread1.start()
thread2.start()
print("Exiting Main Thread")
输出
3、线程合并
join
函数使得主线程等到子线程结束时才退出, join
函数执行顺序是逐个执行每个线程,执行完毕后继续往下执行。
线程对象.join()
因此,join应当放到两个线程启动之间。
import threading
import time
def thre(x, y):
for i in range(x, y):
print(i)
time.sleep(1)
if __name__ == '__main__':
thread1 = threading.Thread(target=thre, args=(1, 10))
thread2 = threading.Thread(target=thre, args=(11, 20))
thread1.start() # 启动线程1
thread1.join()
thread2.start() # 启动线程2
输出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
4、线程同步
如果多个线程共同对某个数据修改,则可能出现不可预料的结果,为了保证数据的正确性,需要对多个线程进行同步。
考虑这样一种情况:一个列表里所有元素都是0,线程"set"从后向前把所有元素改成1,而线程"print"负责从前往后读取列表并打印。
那么,可能线程"set"开始改的时候,线程"print"便来打印列表了,输出就成了一半0一半1,这就是数据的不同步。为了避免这种情况,引入了锁的概念。
- python锁
锁有两种状态——锁定和未锁定。每当一个线程比如"set"要访问共享数据时,必须先获得锁定;如果已经有别的线程比如"print"获得锁定了,那么就让线程"set"暂停,也就是同步阻塞;等到线程"print"访问完毕,释放锁以后,再让线程"set"继续。
使用Thread对象的Lock和Rlock可以实现简单的线程同步,这两个对象都有acquire方法和release方法,对于那些需要每次只允许一个线程操作的数据,可以将其操作放到acquire和release方法之间。
首先,在主函数中创建锁形式
锁名称 = threading.Rlock
锁名称 = threading.Lock
接着,在方法体中使用锁方法
锁名称.acquire() #上锁
锁名称.release() #解锁
import threading
class mythread(threading.Thread):
def run(self):
global x # 声明一个全局变量
lock.acquire() # 上锁
x += 10
print('%s %d' % (self.name, x))
lock.release() # 解锁
if __name__ == '__main__':
x = 0 # 设置全局变量初始值
lock = threading.RLock() # 创建可重入锁
list1 = []
for i in range(5):
list1.append(mythread()) # 创建五个线程,放到同一列表中
for i in list1:
i.start() # 开启列表线程
输出
5、线程守护
如果希望主线程执行完毕之后,不管子线程是否执行完毕都随着主线程一起结束。
我们可以使用daemon函数,它跟join
函数是相反的。
必须在start()
之前调用,默认为False
线程对象.daemon = True
import threading
import time
class myThread(threading.Thread): # 继承父类threading.Thread
def __init__(self, threadID, name, counter):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.counter = counter
def run(self): # 把要执行的代码写到run函数里面 线程在创建后会直接运行run函数
print("Starting " + self.name)
time.sleep(3)
# print_time(self.name, self.counter, 5)
print("Exiting " + self.name)
if __name__ == '__main__':
# 创建新线程
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)
thread1.daemon = True
# 开启线程
thread1.start()
thread2.start()
print("Exiting Main Thread")
输出
6、其他线程模块
Python通过两个标准库thread和threading提供对线程的支持。thread提供了低级别的、原始的线程以及一个简单的锁。
threading 模块提供的其他方法:
- threading.currentThread(): 返回当前的线程变量。
- threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
- threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
Thread类提供了以下方法:
- run(): 用以表示线程活动的方法。
- start(): 启动线程活动。
- join([time]): 等待至线程中止。这阻塞调用线程直至线程的join() 方法被调用中止-正常退出或者抛出未处理的异常-或者是可选的超时发生。
- isAlive(): 返回线程是否活动的。
- getName(): 返回线程名。
- setName(): 设置线程名