线程概述
多线程相似于同时执行多个不同程序,多线程运行有如下长处:
应用线程能够把占据长时间的程序中的工作放到后盾去解决。
用户界面能够更加吸引人,这样比方用户点击了一个按钮去触发某些事件的解决,能够弹出一个进度条来显示解决的进度
程序的运行速度可能放慢
在一些期待的工作实现上如用户输出、文件读写和网络收发数据等,线程就比拟有用了。在这种状况下咱们能够开释一些宝贵的资源如内存占用等等。
线程在执行过程中与过程还是有区别的。每个独立的过程有一个程序运行的入口、程序执行序列和程序的进口。然而线程不可能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行管制。
每个线程都有他本人的一组CPU寄存器,称为线程的上下文,该上下文反映了线程上次运行该线程的CPU寄存器的状态。
指令指针和堆栈指针寄存器是线程上下文中两个最重要的寄存器,线程总是在过程失去上下文中运行的,这些地址都用于标记领有线程的过程地址空间中的内存。
线程能够被抢占(中断)。
在其余线程正在运行时,线程能够临时搁置(也称为睡眠) — 这就是线程的让步。
线程就是在程序运行过程中,执行程序代码的一个分支,每个运行的程序至多都有一个线程
创立多线程:thread
调用thread模块中的start_new_thread()函数来产生新线程
thread.start_new_thread ( function, args[, kwargs] )
function - 线程函数。
args - 传递给线程函数的参数,他必须是个tuple类型。
kwargs - 可选参数。
python多线程的开启
start()
import threading
import time
def task():
time.sleep(1)
print("以后线程:", threading.current_thread().name)
if__name__ =='__main__':
for_inrange(5):
sub_thread = threading.Thread(target=task)
sub_thread.start()
线程之间执行是无序的
主线程会期待所有的子线程完结后才完结
主线程会期待所有的子线程完结后才完结,如果须要能够设置守护主线程
多线程的特点(共享全局变量)
threading.currentThread(): 返回以后的线程变量。
threading.enumerate(): 返回一个蕴含正在运行的线程的list。正在运行指线程启动后、完结前,不包含启动前和终止后的线程。
threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有雷同的后果。
除了应用办法外,线程模块同样提供了Thread类来解决线程,Thread类提供了以下办法:
run(): 用以示意线程流动的办法。
start():启动线程流动。
join([time]): 期待至线程停止。这阻塞调用线程直至线程的join() 办法被调用停止-失常退出或者抛出未解决的异样-或者是可选的超时产生。
isAlive(): 返回线程是否流动的。
getName(): 返回线程名。
setName(): 设置线程名。
自定义线程
自定义线程不能指定target,因为自定义线程外面的工作都对立在run办法外面执行
启动线程对立调用start办法,不要间接调用run办法, 因为这样不是应用子线程去执行工作
import threading
# 自定义线程类
classMyThread(threading.Thread):
# 通过构造方法取接管工作的参数
def__init__(self, info1, info2):
# 调用父类的构造方法
super(MyThread, self).__init__()
self.info1 = info1
self.info2 = info2
# 定义自定义线程相干的工作
def test1(self):
print(self.info1)
deftest2(self):
print(self.info2)
# 通过run办法执行相干工作
def run(self):
self.test1()
self.test2()
创立自定义线程
my_thread = MyThread("测试1","测试2")
启动
my_thread.start()
资源竞争问题
多线程能够共享全局变量
多线程共享全局变量,很不便在多个线程间共享数据
因为多线程同时对全局变量进行操作,很容易呈现资源竞争问题
import threading
# 定义全局变量
g_num =0
# 循环一次给全局变量加1
defsum_num1():
for i in range(1000000):
globalg_num
g_num +=1
print("sum1:", g_num)
# 循环一次给全局变量加1
defsum_num2():
for i in range(1000000):
global g_num
g_num +=1
print("sum2:", g_num)
if__name__ =='__main__':
# 创立两个线程
first_thread = threading.Thread(target=sum_num1)
second_thread = threading.Thread(target=sum_num2)
# 启动线程
first_thread.start()
# 启动线程
second_thread.start()
咱们能够看到多线程同时对全局变量操作数据产生了谬误
多线程同时操作全局变量导致数据可能呈现谬误的起因剖析
两个线程first_thread和second_thread都要对全局变量g_num(默认是0)进行加1运算,然而因为是多线程同时操作,有可能呈现上面状况:
在g_num=0时,first_thread获得g_num=0。此时零碎把first_thread调度为”sleeping”状态,把second_thread转换为”running”状态,t2也取得g_num=0
而后second_thread对失去的值进行加1并赋给g_num,使得g_num=1
而后零碎又把second_thread调度为”sleeping”,把first_thread转为”running”。线程t1又把它之前失去的0加1后赋值给g_num。
这样导致尽管first_thread和first_thread都对g_num加1,但后果依然是g_num=1
多线程资源竞争解决办法
线程同步
线程同步: 保障同一时刻只能有一个线程去操作全局变量 同步: 就是协同步调,按预约的先后秩序进行运行。如:你说完,我再说, 好比现实生活中的对讲机
多个线程同时对同一个全局变量进行操作,会有可能呈现资源竞争数据谬误的问题
线程同步形式能够解决资源竞争数据谬误问题,然而这样有多任务变成了单任务。
互斥锁
threading模块中定义了Lock变量,这个变量实质上是一个函数,能够不便的解决锁定:
创立锁
mutex = threading.Lock()
锁定
mutex.acquire()
开释
mutex.release()
import threading
# 定义全局变量
g_num =0
# 创立全局互斥锁
lock = threading.Lock()
# 循环一次给全局变量加1
def sum_num1():
# 上锁
lock.acquire()
for i in range(1000000):
global g_num
g_num +=1
print("sum1:", g_num)
# 开释锁
lock.release()
# 循环一次给全局变量加1
def sum_num2():
# 上锁
lock.acquire()
for iin range(1000000):
global g_num
g_num +=1
print("sum2:", g_num)
# 开释锁
lock.release()
if __name__ =='__main__':
# 创立两个线程
first_thread = threading.Thread(target=sum_num1)
second_thread = threading.Thread(target=sum_num2)
# 启动线程
first_thread.start()
second_thread.start()
提醒:加上互斥锁,哪个线程抢到这个锁咱们决定不了,哪个线程抢到锁哪个线程先执行,没有抢到的线程须要期待
# 加上互斥锁多任务霎时变成单任务,性能会降落,也就是说同一时刻只能有一个线程去执行
当一个线程调用锁的acquire()办法取得锁时,锁就进入“locked”状态。
每次只有一个线程能够取得锁。如果此时另一个线程试图取得这个锁,该线程就会变为“blocked”状态,称为“阻塞”,直到领有锁的线程调用锁的release()办法开释锁之后,锁进入“unlocked”状态。
线程调度程序从处于同步阻塞状态的线程中抉择一个来取得锁,并使得该线程进入运行(running)状态。
确保了某段要害代码只能由一个线程从头到尾残缺地执行
锁的害处:
多线程执行变成了蕴含锁的某段代码实际上只能以单线程模式执行,效率就大大地降落了
锁应用不好就容易呈现死锁状况
死锁
import threading
import time
# 创立互斥锁
lock = threading.Lock()
# 依据下标去取值, 保障同一时刻只能有一个线程去取值
def get_value(index):
# 上锁
lock.acquire()
print(threading.current_thread())
my_list = [3,6,8,1]
# 判断下标开释越界
if index >=len(my_list):
print("下标越界:", index)
return
value = my_list[index]
print(value)
time.sleep(0.2)
# 开释锁
lock.release()
if __name__ =='__main__':
# 模仿大量线程去执行取值操作
for i in range(30):
sub_thread = threading.Thread(target=get_value,args=(i,))
sub_thread.start()
应用互斥锁的时候须要留神死锁的问题,要在适合的中央留神开释锁
死锁一旦产生就会造成利用的进行响应
线程优先级队列( Queue)
Python的Queue模块中提供了同步的、线程平安的队列类,包含FIFO(先入先出)队列Queue,LIFO(后入先出)队列LifoQueue,和优先级队列PriorityQueue。这些队列都实现了锁原语,可能在多线程中间接应用。能够应用队列来实现线程间的同步。
Queue模块中的罕用办法:
Queue.qsize() 返回队列的大小
Queue.empty() 如果队列为空,返回True,反之False
Queue.full() 如果队列满了,返回True,反之False
Queue.full 与 maxsize 大小对应
Queue.get([block[, timeout]])获取队列,timeout等待时间
Queue.get_nowait() 相当Queue.get(False)
Queue.put(item) 写入队列,timeout等待时间
Queue.put_nowait(item) 相当Queue.put(item, False)
Queue.task_done() 在实现一项工作之后,Queue.task_done()函数向工作曾经实现的队列发送一个信号
Queue.join() 实际上意味着等到队列为空,再执行别的操作
线程,你学废了吗?
发表回复