一、多线程执行
1、使用前先导入 form threading import Thread
2、t = Thread( target = test ) 创建线程
3、t.start( ) 启动线程
4、主线程会等待所有的子线程结束后才结束
5、通过Thread子类完成创建多线程
二、线程共享全局变量
1、与进程不同,线程之间共享全局变量
通过以上实例我们发现,经过两个子线程对全局变量 num 的修改,num 最终的值已经从 100 变成了 113,进一步证明了线程之间是共享全局变量的
2、由于线程之间共享全局变量,当子线程对全局变量进行操作时,可能会导致一些BUG的出现
如图,经过两个子线程的运行,最终全局变量 g_num 的值应为2000000,但是运行结果如下:
解决方法一: 主线程等待1秒后,再开启第二个线程
解决方法二:定义一个标志位,通过标志位状态来执行不同的线程
解决方法三:互斥锁
①当多个线程几乎同时修改某个共享数据时,需要进行同步控制,线程同步控制能够保证多个线程安全访问竞争资源,最简单的同步机制就是引入互斥锁,
②互斥锁为资源引入一个状态:锁定 / 非锁定
③某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其他的线程不能更改,直到该线程释放资源,将资源状态改为“非锁定”,其他的线程才能再次锁定该资源,互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程操作下,数据的正确性
④互斥锁使用前先导入
使用互斥锁时需要注意逻辑性,避免出现死锁
可重用锁:RLock(),出现多线程竞争资源时,如果无法熟练使用互斥锁,可以选择使用可重用锁,
以上实例,使用互斥锁会出现自锁现象,而使用可重用锁则没有问题。
三、线程之间的局部变量不共享
1、
如图, threading.current_thread( ).name 可以获得线程名
2、
我们发现,虽然两个线程都去同一个函数中执行任务,但是局部变量g_num并不共享
四、Condition用法
- acquire():线程锁,只有获取线程锁资格,该线程才能继续执行
- release():释放锁,释放之后,其他线程才可以获取线程锁
- wait():线程挂机,直到收到notify通知、或者超时(可选的,浮点数,单位是s)才会被唤醒,继续执行。wait()必须在获取线程锁的前提下才可以使用,否则会触发RuntimeError。
- notify(n=1):唤醒其他挂机线程,默认唤醒一个正在等待该condition的线程,最多可唤醒n个挂机的线程。notify()必须在获取线程锁的前提下才可以使用,否则会触发RuntimeError。,notify()不会主动释放线程锁。
- notifyAll():如果wait状态线程较多,使用该方法唤醒全部线程
五、Event用法
Python提供了Event对象用于线程间通信,它是由线程设置的信号标志,如果信号标志位为假,则线程等待直到信号被其他线程设置成真。这一点似乎和windows的event正好相反。 Event对象实现了简单的线程通信机制,它提供了设置信号,清除信号,等待等用于实现线程间的通信。
- 设置信号:使用Event的set()方法可以设置Event对象内部的信号标志为真。Event对象提供了isSet()方法来判断其内部信号标志的状态,当使用event对象的set()方法后,isSet()方法返回真.
- 清除信号:使用Event对象的clear()方法可以清除Event对象内部的信号标志,即将其设为假,当使用Event的clear方法后,isSet()方法返回假
- 等待:Event对象wait的方法只有在内部信号为真的时候才会很快的执行并完成返回。当Event对象的内部信号标志位假时,则wait方法一直等待到其为真时才返回。
# make thread exit nicely
class MyThread9(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
global event
while True:
if event.isSet():
logging.warning(self.getName() + " is Running")
time.sleep(2)
else:
logging.warning(self.getName() + " stopped")
break;
event = threading.Event()
event.set()
def Test9():
t1=[]
for i in range(6):
t1.append(MyThread9())
for i in t1:
i.start()
time.sleep(10)
q =raw_input("Please input exit:")
if q=="q":
event.clear()
if __name__=='__main__':
Test9()
六、守护线程与非守护线程
1、守护线程:线程一直运行而不阻塞主程序,即主程序不会等待该线程结束而结束,一旦所有的非守护线程结束,主程序就会结束。
2、非守护线程:主程序会在非守护线程结束之后再结束,即非守护线程会阻塞主程序。
3、默认情况下,线程都是非守护线程,使用setDaemon()方法,选择设置该线程是否为守护线程。
from threading import Thread
import time ,threading
def task(t):
time.sleep(t)
print(threading.currentThread().getName(),"end")
print("开始")
for i in range(5):
t=Thread(target=task,args=(i,))
t.setName("线程"+i)
t.setDaemon(True) # 设置线程为守护线程,即主程序不会等待该线程结束
t.start()