线程
线程
在一个进程内可以使用多个线程,每个线程执行不同的任务。
如QQ进程,其下可以同时进行收发信息、播放音乐、查看空间,这些都是用线程并发实现的。
特点
1.一个进程中可以并发多个线程执行不同的任务。
2.线程可以共享进程的资源。
3.线程之间可以共享数据。
4.线程是包含在进程中的,线程崩溃会引发进程崩溃,从而导致该进程其他线程也崩溃。
创建线程
可使用两种方式创建线程。
1.threading模块Thread类创建线程。
2.使用Thread子类创建线程。
threading模块Thread类创建线程
语法
Thread([group [,target [,name [,args [,kwargs]]]]])
'''
group:值为None,该参数未被使用。
target:表示可调用对象,可以为线程需要执行的函数,默认值为None,表示不调用任何内容。
name:当前线程名称,默认为Thread-n
args:传递给target函数的参数,是一个元组。
kwargs:传递给target函数的参数,是一个字典。
'''
案例
import threading
import time
def thread():
for i in range(3):
time.sleep(1)
print('thread name is %s' %threading.current_thread().name)
if __name__ == '__main__':
print('主线程启动,创建4个线程')
#使用列表生成式创建4个线程
threads = [threading.Thread(target=thread) for i in range(4)]
for t in threads:
t.start()
for t in threads:
t.join()
print('主进程结束')
'''
out:
主线程启动,创建4个线程
thread name is Thread-2
thread name is Thread-1
thread name is Thread-4
thread name is Thread-3
thread name is Thread-3
thread name is Thread-1
thread name is Thread-2
thread name is Thread-4
thread name is Thread-2
thread name is Thread-4
thread name is Thread-1
thread name is Thread-3
主线程结束
'''
#注意每个线程执行的顺序是乱序的。
使用Thread子类创建线程
Thread线程类跟Process进程类使用方式相似。
案例
from threading import Thread
import time
class SubThread(Thread):
def run(self):
for i in range(3):
time.sleep(1) #因为执行的太快所以等待1秒钟
msg = '子线程'+self.name+'正在执行任务,此时i='+str(i)
print(msg)
if __name__ == '__main__':
print('主线程启动,安排两个线程工作')
t1 = SubThread() #这里没有传递参数,所以没有创建__init__()方法进行赋值。
t2 = SubThread()
t1.start() #开启线程,会自动调用执行run()方法
t2.start()
t1.join()
t2.join()
print('主线程结束')
'''
out:
主线程启动,安排两个线程工作
子线程Thread-2正在执行任务,此时i=0
子线程Thread-1正在执行任务,此时i=0
子线程Thread-2正在执行任务,此时i=1
子线程Thread-1正在执行任务,此时i=1
子线程Thread-2正在执行任务,此时i=2
子线程Thread-1正在执行任务,此时i=2
主线程结束
'''
#两个子进程是同时开始执行任务,所以i有两次都相等
线程间通信
进程跟进程之间不能直接通信,需要队列或管道当作中间人让进程之间互相通信。
线程之间可以直接进行通信。
案例
from threading import Thread
#定义全局变量使其在进程之间传递
g_num = 100
def plus():
print('子线程1开始执行任务')
global g_num
g_num += 1
print('全局变量g_num初始值为100,加1,此时为 %d' % g_num)
print('子线程1结束')
def minus():
print('子线程2开始执行任务')
global g_num
g_num -= 1
print('全局变量g_num初始值为100,减1,此时为 %d' % g_num)
print('子线程2结束')
if __name__ == '__main__':
print('主线程启动,产生两个线程执行任务')
p1 = Thread(target=plus)
p2 = Thread(target=minus)
p1.start()
p2.start()
p1.join()
p2.join()
print('主线程结束')
'''
out:
主线程启动,产生两个线程执行任务
子线程1开始执行任务
全局变量g_num初始值为100,加1,此时为 101
子线程1结束
子线程2开始执行任务
全局变量g_num初始值为100,减1,此时为 100
子线程2结束
主线程结束
'''
#可以看到,全局变量被进程1加1,成为101,被进程2减1,成为100,两者共享使用了全局变量g_num
互斥锁
给线程加锁,当线程使用某块资源时先加锁,使用完后再解锁,锁定期间其他线程不能修改此块资源。
使用互斥锁能够防止多个线程同时读写内存中的某个区域,从而造成混乱。
使用互斥锁
使用threading模块中使用Lock类实现。
Lock类有两个方法:
acquire()锁定
release()解锁
语法
m = threading.Lock() #创建锁对象
m.acquire([blocking]) #锁定
m.release() #解锁
'''
acquire([blocking]):给线程加锁,让其放心的读写某块资源,如果设置blocking参数为Flase,则无法获取锁定状态时会返回False,成功锁定会返回True
release():解锁,与锁定配套使用,如果该线程未被成功锁定,其他线程调用此方法会报错。
'''
案例
#用线程模仿10个人购买电影票
from threading import Thread,Lock
import time
#定义全局变量电影票100张
g_n = 100
def task():
global g_n
#上锁
mutex.acquire()
time.sleep(1)
g_n -= 1
print('购买成功,剩余%d张电影票' % g_n)
mutex.release()
if __name__ == '__main__':
mutex = Lock()
t_list = []
#创建10个线程,这10个线程相当于10个人,将创建的线程对象放入列表
for i in range(10):
t = Thread(target=task)
t_list.append(t)
t.start()
#从列表中取出线程对象,让其进程等待线程执行完毕
for t in t_list:
t.join()
'''
out:
购买成功,剩余99张电影票
购买成功,剩余98张电影票
购买成功,剩余97张电影票
购买成功,剩余96张电影票
购买成功,剩余95张电影票
购买成功,剩余94张电影票
购买成功,剩余93张电影票
购买成功,剩余92张电影票
购买成功,剩余91张电影票
购买成功,剩余90张电影票
'''
死锁
使用互斥锁时,要避免死锁。
死锁通俗讲解就是进程A锁住了资源1并等待使用资源2,而进程B锁住了资源2并等待使用资源1,造成了互相等待对方。
使用队列实现线程之间通信
通过queue模块的Queue来实现。
生产者消费者模式
线程间通信通常应用于生产者消费者模式,生产数据的函数或模块称为生产者,处理数据的函数或模块称为消费者,生产者(函数)向仓库(队列)放入商品,消费者从仓库取出商品。
案例
from queue import Queue
from threading import Thread
import time
import random
#定义生产者类
class Producer(Thread):
def __init__(self,name,queue):
#调用父类的init方法
Thread.__init__(self,name=name)
self.data = queue
def run(self):
#定义五个生产者
for i in range(5):
print('生产者%s 将产品%d加入队列' %(self.getName(),i))
#将i加入队列
self.data.put(i)
time.sleep(random.random())
print('生产者%s完成任务!'%(self.getName()))
#定义消费者类
class Consumer(Thread):
def __init__(self,name,queue):
#调用父类的init方法
Thread.__init__(self,name=name)
self.data = queue
def run(self):
#定义五个生产者
for i in range(5):
#取出数据
value = self.data.get()
print('消费者%s 将产品%d从队列中取出' %(self.getName(),value))
time.sleep(random.random())
print('消费者%s完成任务!'%(self.getName()))
if __name__ == '__main__':
print('主线程开始')
queue = Queue()
#创建生产者类对象
producer = Producer('Producer',queue)
consumer = Consumer('Consumer',queue)
producer.start()
consumer.start()
producer.join()
consumer.join()
print('主线程结束')
'''
out:
主线程开始
生产者Producer 将产品0加入队列
消费者Consumer 将产品0从队列中取出
生产者Producer 将产品1加入队列
消费者Consumer 将产品1从队列中取出
生产者Producer 将产品2加入队列
生产者Producer 将产品3加入队列
消费者Consumer 将产品2从队列中取出
消费者Consumer 将产品3从队列中取出
生产者Producer 将产品4加入队列
消费者Consumer 将产品4从队列中取出
消费者Consumer完成任务!
生产者Producer完成任务!
主线程结束
'''
学习来自:《python从入门到项目实践》明日科技 第十六章
今天的学习是为了以后的工作更加的轻松!