天天看点

python的多线程简单实例理解

一直以来对于线程的理解是比较模糊的,之前在用php的时候,由于php是不直接支持多线程,要用php_threads的扩展。最近由于公司项目的后台需要要到多线程,故学习了一下python的多线程,下面通过一个简单的例子说明一下。

在实际中,比如说要听音乐,看电影,做其他的任务,按照单例的话,只能是一件做接着一件做。如果每件事都是很耗时的,那就会影响效率。同理在编程的世界里。同样有很多的相似的逻辑业务。比如群发邮件,按照单例我们的程序就会用循环的方式,发完一个再发一个。但是,发邮件的这个过程却是要耗时的,这样样子的浪费多长的时间才能将大量的邮件发完。而且程序还有一个超时时间限制呢!(虽然可以设置为无限制)。可想而知,这样子的话,就会造成效率低,甚至发送不成功。

下面进入主题

1、看一个简单的例子

#!D:\Python43\python.exe
# -*- coding: UTF-8 -*-

from time import ctime,sleep

#听音乐
def music(musicname):
	for i in range(2) :
		print("我正在听音乐叫%s,时间是:%s" %(musicname,ctime()))
		sleep(5)

#看电影
def movie(moviename):
	for j in range(2) :
		print("我正在看电影叫%s,时间是:%s" %(moviename,ctime()))
		sleep(2)

#主线程
if __name__ =='__main__':
	music('当爱已成往事')
	movie('中华英雄')

	print('所有的娱乐结束,时间:%s' %ctime())
           
python的多线程简单实例理解
python的多线程简单实例理解

定义两个函数,分别做两个任务,听音乐和看电影

music函数就是任务打印出听音乐这句话(程序执行非常快,纳秒级),这个时间是在15:36:56.然后等待5s,再去打印出听音乐这句话,时间是15:37:01,然后再睡眠等待5s.所以在这个过程任务的用时为10s.

接着到执行movie函数,睡眠时间2s.这个过程用时就是4s

最后,执行主程序的结束。我的娱乐任务完成。所以在这个过程他是按照程序单线程一直执行的。效率非常低。

2、多线程引入(非阻塞)

</pre><pre name="code" class="html">def music(musicname):
	for i in range(2) :
		print("我正在听音乐叫%s,时间是:%s" %(musicname,ctime()))
		sleep(2)

#看电影
def movie(moviename):
	for j in range(2) :
		print("我正在看电影叫%s,时间是:%s" %(moviename,ctime()))
		sleep(2)

threads=[]
t1 = threading.Thread(target=music,args=(u'一路顺风',))
threads.append(t1)
t2 = threading.Thread(target=movie,args=(u'让子弹飞',))
threads.append(t2)

#主线程
if __name__ =='__main__':
	#循环执行子线程
	for t in threads :
		#将父线程设置为了守护线程。
		#根据setDaemon()方法的含义,
		#父线程打印内容后便结束了,
		#不管子线程是否执行完毕了。
		t.setDaemon(True)
		t.start()

	print('所有的娱乐结束,时间:%s' %ctime())
           
python的多线程简单实例理解
python的多线程简单实例理解

可以看到,在执行主程序的时候,三个任务基本是同时完成,而且music和movie只执行一遍,任务还没全部完成。为什么呢?

首先过程是先把所有的子线程启动,即musi和movie,这里是两条不同路,接着同时执行主程序的

print('所有的娱乐结束,时间:%s'%ctime()),又是一条路

主程序结束,就会将退出,同时子线程也退出,不管你的任务有没有完成。所以music和movie在启动并打印出那句话时,在睡眠等待,那边的主程序也已经执行到打印操作,所以主程序就退出,同时也通知正在睡眠等待的子线程退出。所以他们只执行一次打印任务,就被迫退出。为什么会被迫退出呢?那与我们的逻辑业务不符合啊。不急,接着往下看。原因就在于这个setDaemon(True)就是将主进程设置为守护线程,根据setDaemon()方法的含义,父线程打印内容后便结束了,不管子线程是否执行完毕了,都退出

3、多线程(阻塞)

#!D:\Python43\python.exe
# -*- coding: UTF-8 -*-

from time import ctime,sleep
import threading

#听音乐
def music(musicname):
	for i in range(2) :
		print("我正在听音乐叫%s,时间是:%s" %(musicname,ctime()))
		sleep(5)

#看电影
def movie(moviename):
	for j in range(2) :
		print("我正在看电影叫%s,时间是:%s" %(moviename,ctime()))
		sleep(2)

threads=[]
t1 = threading.Thread(target=music,args=(u'一路顺风',))
threads.append(t1)
t2 = threading.Thread(target=movie,args=(u'让子弹飞',))
threads.append(t2)

#主线程
if __name__ =='__main__':
	#循环执行子线程
	for t in threads :
		#将父线程设置为了守护线程。
		#根据setDaemon()方法的含义,
		#父线程打印内容后便结束了,
		#不管子线程是否执行完毕了。
		<span style="color:#ff0000;">#t.setDaemon(True)</span>
		t.start()

	<span style="color:#ff0000;">for t in threads :
		t.join()</span>

	print('所有的娱乐结束,时间:%s' %ctime())
           
python的多线程简单实例理解
python的多线程简单实例理解

t.setDaemon(True)</span>
           
屏蔽掉,同时再加入一个for循环
           
for t in threads :
<span style="white-space:pre">	</span>t.join()
           

在执行主程序时,启动两个子线程,在15:18:09 同时执行一遍打印任务,两个子线程在睡眠等待中,时间到了,有同时个字执行一片打印任务。再睡眠等待2s后,再执行主程序的打印任务。

其实起作用的就是这个join()函数,这是一个阻塞函数,要子线程执行完了才能继续往下执行主程序(或其他程序)。

所以在for循环中,join()阻塞先等待第一个子线程结束后,在继续等待第二个子线程结束,如果第二个或者后面提前比前面的结束,则就会下一个join(),不需要等待(因为join()检查到该线程已经结束)。循环之后,就会往下执行主程序。所以整个过程所用的时间就是用时最长的任务的时间。所有的几乎是同时进行。

所以我们可以知道,setDaemon是与join()相对的。功能上基本是相反的