一、什么是线程,什么是进程
首先,线程和进程都是一个时间段的描述,是cpu工作时间段的描述。
线程是程序执行流的最小单元,线程有就绪,阻塞和运行三种状态,就绪就是指线程具备所有的运行条件,逻辑上可以运行,只需等待cpu分配时间片即可,运行状态就是指线程占有cpu时间片,正在运行,阻塞是指线程在等待一个事件(如某个信号量),逻辑上不可执行,每一个程序至少有一个线程。线程是程序中一个单一的顺讯控制流程,进程内有一个相对独立的、可调度的执行单元,是系统独立调度和分派cpu的基本单位指令运行时的调度单位,在单个程序中同时运行多个线程完成不同的工左,称为多线程
进程是cpu分配资源的最小单位,也有就绪,运行,阻塞三种状态。
二,python多线程
1、使用_thread模块创建两个线程
import time
import _thread
def function_one():
for i in range(5):
time.sleep(1)
print('function_one run')
def function_two():
for i in range(5):
time.sleep(1)
print('function_two run')
def main():
#创建两个线程
try:
thread1 = _thread.start_new_thread(function_one,())
thread2 =_thread.start_new_thread(function_two,())
except Exception as Erorr:
print(Erorr)
else:
print('线程创建成功')
time.sleep(10)
if __name__ == '__main__':
main()
2、使用threading模块创建两个线程
import time
import threading
def function(times):
for i in range(6):
time.sleep(1)
print('function_%s run %s'%(times,i))
def main():
#创建两个线程
try:
t1 = threading.Thread(target=function,args=('one',))
t2 = threading.Thread(target=function,args=('two',))
except Exception as Erorr:
print(Erorr)
else:
print('线程创建成功')
#启动两个线程
t1.start()
t2.start()
t1.join(20) #主线程结束前,t1子线程还未结束,主线程最多再等待t1子线程20秒。
t2.join() #主线程结束前,t2子线程还未结束,主线程一直得等到t2子线程结束。
if __name__ == '__main__':
main()
三,线程锁
当我们使用多线程岁同一块内存空间进行反复操作时,可能会导致内存数据混乱,例如如下代码,我们总会将第50张票在两窗口进行两次售卖的情况
#在一个公园有两个窗口,一天售卖50张票,使用两个线程来模拟这个情况
import threading
import time
ticket = 50
def ticket_sales(times):
global ticket
while ticket > 0:
print('窗口%d售出第%d张票'%(times,ticket))
time.sleep(0.1)
ticket = ticket - 1
def main():
#创建两个线程
try:
t1 = threading.Thread(target=ticket_sales,args=(1,))
t2 = threading.Thread(target=ticket_sales,args=(2,))
except Exception as Erorr:
print(Erorr)
else:
print('线程创建成功')
#启动两个线程
t1.start()
t2.start()
t1.join()
t2.join() #主线程结束前,t2子线程还未结束,主线程一直得等到t2子线程结束。
if __name__ == '__main__':
main()
我们对上面的ticket使用线程锁。
#在一个公园有两个窗口,一天售卖50张票,使用两个线程来模拟这个情况
import threading
import time
ticket = 50
lock = threading.Lock() # 创建线程锁
def ticket_sales(times):
global ticket
while ticket > 0:
lock.acquire()
if ticket>0:
print('窗口%d售出第%d张票'%(times,ticket))
time.sleep(0.1)
ticket = ticket-1
lock.release()
time.sleep(0.1)
def main():
#创建两个线程
try:
t1 = threading.Thread(target=ticket_sales,args=(1,))
t2 = threading.Thread(target=ticket_sales,args=(2,))
except Exception as Erorr:
print(Erorr)
else:
print('线程创建成功')
#启动两个线程
t1.start()
t2.start()
t1.join()
t2.join() #主线程结束前,t2子线程还未结束,主线程一直得等到t2子线程结束。
if __name__ == '__main__':
main()
四、线程优先级队列
生产者-消费者模型
生产者,扫描某个目录下的所有文件,并将其路径放入长度为4的队列中
消费者,对队列中的文件,按照先进先出的顺序进行拷贝到指定目录
import threading
import time
from queue import Queue
import os
def SearchFileAbsPath(dirname):
dirname = os.path.abspath(dirname) #
filenames = list()
for root, dirs, files in os.walk(dirname, topdown=False): # 扫描一层目录
for name in files:
filenames.append(root + os.path.sep + name) # 每一个文件的绝对路径放入列表
return filenames
class Producer(threading.Thread):
def __init__(self,filenames,queue):
super(Producer,self).__init__()
self.queu = queue
self.filenames = filenames
def run(self):
while self.filenames:
if self.queu.full():
print('队列已满,生产者请等待!!!')
else:
file = self.filenames[0]
with open(file,'r') as file_it:
file_content = file_it.read()
#队列中存放了文件绝对路径和文件对应的内容组成的字典
self.queu.put({file:file_content})
print(file,'文件内容入队成功')
time.sleep(1)
class Consumer(threading.Thread):
def __init__(self,dirname,filenames,queue):
super(Consumer,self).__init__()
self.dirname= dirname
self.queu = queue
self.filenames = filenames
def run(self):
while self.filenames:
if self.queu.empty():
print('队列空,消费者请等待!!!')
else:
file_dict = self.queu.get()
for key,values in file_dict.items():
with open(self.dirname+os.path.sep+os.path.split(key)[1],'w') as file_it:
file_it.write(values)
print(key,'复制成功!!!')
self.filenames.pop(0) #将已经完完成拷贝的文件从列表中删除,保证while循环正常退出
time.sleep(1)
def main():
que = Queue(4)
filenames = SearchFileAbsPath('/root/PycharmProjects/day13')
producer = Producer(filenames,que)
consumer = Consumer('/root/Desktop/hello',filenames,que)
producer.start()
consumer.start()
producer.join()
consumer.join()
if __name__ == '__main__':
main()
五、使用线程池
我们在使用多线程处理多个任务时,占有时间最多的并不是,我们程序的实际执行时间,而是我们的线程之间的上线文切换时间,这样就导致我们多线程并没有达到预想的效果。我们使用线程池,可以并发的同事执行多个任务,免去了大量线程之间的上线下文切换,最重要的是,不需要线程线程每次都进行资源的申请与释放。
# (python3.4以后的可以使用)
# *******************线程池的第一种方式***********************
from concurrent.futures import ThreadPoolExecutor
import time
def hello():
print(time.ctime())
time.sleep(1)
def main():
pool = ThreadPoolExecutor(max_workers=3)
pool.submit(hello)
pool.submit(hello)
pool.submit(hello)
time.sleep(5)
if __name__ == '__main__':
main()
输出:
Sat Jun 16 04:33:17 2018
Sat Jun 16 04:33:17 2018
Sat Jun 16 04:33:17 2018
从输出我们可以看出,线程池中的三个线程完成了并发。
我们对上面的消费者和生产者模型使用线程池。
from concurrent.futures import ThreadPoolExecutor
import time
import os
def SearchFileAbsPath(dirname):
dirname = os.path.abspath(dirname) #
filenames = list()
for root, dirs, files in os.walk(dirname, topdown=False): # 扫描一层目录
for name in files:
filenames.append(root + os.path.sep + name) # 每一个文件的绝对路径放入列表
return filenames
def cpyfile(dirname,filename):
"""
:param dirname: 拷贝后的文件放在的位置
:param filename: 存放文件绝对路径
:return:
"""
with open(filename, 'r') as file_it:
file_content = file_it.read()
with open(dirname + os.path.sep + os.path.split(filename)[1], 'w') as file_it:
file_it.write(file_content)
print(filename, '复制成功!!!')
time.sleep(1)
def main():
filenames = SearchFileAbsPath('/root/PycharmProjects/day13')
pool = ThreadPoolExecutor(max_workers=2)
for filename in filenames:
pool.submit(cpyfile,'/root/Desktop/hello',filename)
if __name__ == '__main__':
main()