python之线程
概念
线程是多任务编程方式之一,可以使用计算机的多核资源。
线程又称为轻量级的进程,在并发执行上和进程相同。但是一个进程中可以包含多个线程,这些线程共享线程的运行环境。
线程和进程的比较:
1 进程的创建开销大,而线程的创建开销小。
2 进程间的资源共享,只能通过进程间通信。而同一进程下线程间的资源共享,就像使用全局变量一样。
3 多个功能独立的程序需要成为不同的进程。而不能通过多线程凑成一个进程。
4 进程空间独立性高,安全性也高,也较少使用同步和互斥方法。而多个线程间因为数据共享,所以同步和互斥几乎是数据必须必须的。
5 线程也有自己的独立资源,比如说ID,资源集。
创建线程
import threading
t = threading.Thread(target, args, kwargs, name)
t.name 线程名字
t.setName() :设置线程名称
t.getName() : 获取名称
t.join(n) :主线程阻塞等待分支线程退出,n为超时时间
t.start() :启动线程
t.Daemon : daemon属性
如果在进程中创建了多个线程,当主进程结束时,会根据子进程的daemon属性来来处理不同的情况:
1 如果某个子线程的daemon为False,那么主线程结束时会检测这个子线程是否结束,如果子线程没有结束,那么主线程会等待子线程运行完后再退出。
2 如果某个子线程的daemon为True,那么主线程结束时不会对这个子线程进行检查而直接退出,同时daemon为True的子线程将随主线程一起退出,不论它们是否运行完成。
属性Daemon的默认值为False,如果要设置,必须在调用start()方法启用前。
t.is_Daemon() :判断Daemon的状态
t.is_alive() : 判断线程状态
import threading
from time import sleep, ctime
# 听歌
def music(name):
print('Listen music %s at the %s '%(name, ctime()))
sleep(2)
# 看书
def read(name):
print('Read book %s at the %s'%(name, ctime()))
sleep(5)
if __name__ == '__main__':
threads = []
t1 = threading.Thread(target=music, args=('James',))
threads.append(t1)
t2 = threading.Thread(target=read, args=('Wade',))
threads.append(t2)
# 启动线程
for t in threads:
t.start()
# 回收线程
for t in threads:
t.join()
多线程类的创建
import threading from time import sleep, ctime exitFlag = 0 class MyThread(threading.Thread): def __init__(self, threadID, name, counter): threading.Thread.__init__(self) self.threadID = threadID self.name = name self.counter = counter def run(self): print('starting' + self.name) print_time(self.name, self.counter, 5) print('ending' + self.name) def print_time(theadName, delay, counter): while counter: if exitFlag: (threading.Thread).exit() sleep(delay) print("%s at the time:%s"%(theadName, ctime())) counter -= 1 if __name__ == '__main__': t1 = MyThread(1, 'Mythread-1', 1) t2 = MyThread(2, 'Mythread-2', 2) t1.start() t2.start() t1.join() t2.join() print('主线程退出!!!')
线程间的通信
线程间的通信一般是使用全局变量,但是这种方法会带来资源的争夺,所有线程间的共享资源一般需要同步互斥机制。
所以为了解决这个问题,我们需要使用2种方法。
方法1: 锁
lock
lock = threading.Lock()
lock.acquire()
lock.release()
import threading
a = b = 0
lock = threading.Lock()
def value():
while True:
lock.acquire()
if a != b:
print('a = %d, b = %d'%(a, b))
lock.release()
if __name__ == '__main__':
t = threading.Thread(target=value)
t.start()
while True:
lock.acquire()
a += 1
b += 1
lock.release()
t.join()
'''
结果是永远也不会发生a!=b,只能在a,b同时修改后读取a,b的值
'''
方法2:事件 event
python事件主要是主线程控制其他线程的执行,主要提供了wait,set,clear方法。
事件的处理机制:全局定义了一个''Flag'',如果Flag为False,那么当程序执行到event.wait方法就会阻塞,如果Flase为True,那么event.wait方法时将不会阻塞.。
clear:将Flag定义为False。
set:将Flag定义为True。
线程间通信:
使用event可以让以一个线程等待其他线程的通知,
e = threading.Event()
e.set() 设置标志位 设置Flag其为True
e.wait() 等待设置标志位 等待标志变为True后执行,当为False时候阻塞等待
e.clear() 清空标志位 设置Flag其为False
import threading
import random
import time
e = threading.Event()
a = 500
def fun():
while True:
time.sleep(2)
e.wait()
global a
print('a = %d'%a)
a -= random.randint(1, 100)
if __name__ == '__main__':
t = threading.Thread(target=fun)
t.start()
while True:
time.sleep(1)
a += random.randint(0, 10)
if a > 100:
e.set()
else:
e.clear()
t.join()
'''
主线程加数字0-10
子线程减数字0-100
当数字大于100时,其Flag一直为True,当小于100时,其Flag为Flase,一直阻塞无法打印
'''
'''
只有当判断输入q时,才会改变Flag为False,从而结束子线程循环,结束程序
'''
import threading
import time
class MyThread9(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
global event
while True:
if event.isSet():
print(self.getName() + ' is Running!')
time.sleep(2)
else:
print(self.getName() + ' Stop!!')
break
event = threading.Event()
event.set()
def func():
t1 = []
t1.append(MyThread9())
for i in t1:
i.start()
time.sleep(10)
q = input('请输入退出:')
if q == 'q':
event.clear()
for i in t1:
i.join()
print('all over!!!')
if __name__ == '__main__':
func()