线程:thread
线程也是多任务编程的一种方法,可以使用计算机多核资源。
线程是计算机核心分配的最小单位
线程又称为轻量级进程,在创建和删除时消耗的计算机资源少
线程和进程关系:
1, 一个进程中可以包含多个线程
2, 进程中的所有线程共享进程的空间资源。(空间、全局变量、分配的内存等)
3, 如果一个进程里面只有一个线程,即单线程情况下,线程和进程等同
4, 线程也有自己的特有属性:指令集,TID等,没有终端命令查看
线程模块:threading
import threading
t = Thread()
功能:创建线程
参数:target 线程函数
args 元祖, 给线程函数位置传参
kwargs 字典,给线程函数字典传参
name 给线程取名字(默认Thread-1)
返回: 线程对象
t.start() 启动线程
t.join() 回收线程
t.is_alive()查看线程状态
t.name 查看线程名称
t.setName('thread_name') 为线程设置名称
threading.currentThread()得到线程对象
t.daemon:
默认false, 主线程执行完毕,不会影响分支线程的执行;
如果设置为True,则主线程执行完毕,其他线程也会终止
t.isDaemon():
判断daemon属性是True还是False
设置daemon方法:
t.daemon = True 或者
t.setDaemon(True)
eg: 线程创建案例
#!/usr/bin/python
#coding:utf-8
import threading
from time import sleep
#定义函数,用于循环打印
def play_music():
while True:
sleep(2)
print('一剪梅.mp3')
#创建线程
t1 = threading.Thread(target = play_music)
#启动线程
t1.start()
#主线程
while True:
sleep(2)
print('梁静茹.wav')
t1.join()
eg2: 测试线程方法
#!/usr/bin/python
#coding:utf-8
import threading
from time import sleep
#自定义函数,打印线程名称,睡眠sec秒
def thread_info(sec):
print('thread attribute testing',threading.currentThread().getName())
sleep(sec)
print('thread end.',threading.currentThread().getName())
#循环创建线程
thread_lst = []
for i in range(4):
t = threading.Thread(name='thread'+str(i), target = thread_info, args = (3,))
t.start()
thread_lst.append(t)
#循环回收线程,且打印状态
for i in thread_lst:
print('name:',i.name,'alive:',i.is_alive())
i.join()
print('father process end....')
eg3: 测试daemon
#!/usr/bin/python
#coding:utf-8
import threading
from time import sleep
print('主线程开始')
#自定义函数
def play():
for _ in range(5):
print('我是子线程', threading.currentThread().getName())
sleep(3)
#创建子线程
t = threading.Thread(target = play, name = 'thread_testing')
#设置t.daemon属性为True,设置为true后,主线程结束,则子线程结束
print('thread daemon status:',t.isDaemon())
t.setDaemon(True)
print('thread daemon status:',t.isDaemon())
t.start()
t.join(2)
#此时主线程结束,观察子线程状态
print('主线程结束')
线程间通信
因为线程是共享全局变量和内存,所以可以通过全局变量进行通信
#!/usr/bin/python
#coding:utf-8
import threading
from time import sleep
global a
def fun_t1():
for _ in range(5):
sleep(1)
a = 100
print('a = ',a)
def fun_t2():
for _ in range(5):
sleep(1.1)
a = 200
print('a = ',a)
t1 = threading.Thread(target = fun_t1,name = 'thread1')
t2 = threading.Thread(target = fun_t2,name = 'thread2')
t1.start()
t2.start()
t1.join()
t2.join()
进程和线程对比
1, 两者都是多任务编程方式,均可使用计算机多核资源
2, 进程的创建和删除比线程消耗更多的资源
3, 进程空间独立,数据安全性更好操作,有专门的进程间通信方式。(磁盘/Socket/管道/队列/共享内存/信号/信号量)
4, 线程使用全局变量通信,往往要和同步互斥机制配合,防止产生资源的争夺
5, 一个进程可以包含多个线程,线程共享进程资源。
6, 进程线程都有自己的特有资源。
使用场景
线程:需要创建较多的并发,但任务比较简单;
如果多个业务有关联性,数据资源重叠较多,要考虑到线程锁是否需要更复杂逻辑
python中,线程不适用计算密集型并发程序
进程:多个任务并无关联性;
创建自定义线程类
1, 继承Thread类
2, 重写run方法
#!/usr/bin/python
#coding:utf-8
from time import sleep, ctime
from threading import Thread
#自定义函数
def player(filename,sec):
for _ in range(3):
sleep(sec)
print('playing ',filename,'time:',ctime())
#自定义类,继承Thread类
class myThread(Thread):
def __init__(self,fun,args,name = 'tony'):
super().__init__()
self.fun = fun
self.args = args
self.name = name
def run(self):
self.fun(*self.args)
#创建进程
t = myThread(player, ('我的中国心',3))
t.start()
t.join()
线程中同步互斥方法:
Event:线程事件
e = Event()
e.set()
e.wait()
e.clear()
e.is_set()
Lock: 线程锁
lock = Lock() 创建线程锁
lock.acquire() 加锁
lock.release() 解锁
with lock....
pass
condition : 条件变量
con = Condition() 创建条件对象
con.acquire() 对资源加锁,加锁后,其他资源再次加锁则阻塞
con.release() 对资源解锁,用法同Lock()
con.wait() wait函数会先解锁(release()),然后让线程处于等待通知的阻塞状态
wait函数只能在加锁的状态下使用;
con.notify() 发送通知,线程接收到通知后,结束wait阻塞,并且执行acquire()加锁操作
#!/usr/bin/python3
#coding:utf-8
import threading
from time import sleep
con = threading.Condition()
#定义增长函数
def Grow():
global n
n = 0
con.acquire()
for _ in range(15):
print('千山鸟飞绝,万径人踪灭',n)
sleep(1)
n += 1
#当n>=3的时候,执行notify(),发送通知,让对方结束wait阻塞,并加锁; 自己紧接着执行wait()阻塞;
if n >= 3:
con.notify()
con.wait()
con.release()
#定义下降函数
def Down():
global n
n = 0
con.acquire()
for _ in range(15):
print('孤舟蓑笠翁,独钓寒江雪',n)
sleep(1)
n -= 1
#当n<=0的时候,发送通知,并阻塞
if n <= 0:
con.notify()
con.wait()
con.release()
t1 = threading.Thread(target=Grow)
t2 = threading.Thread(target=Down)
t1.start()
t2.start()
t1.join()
t2.join()