python多线程 不在main_Python多线程/单线程

定义:

进程:资源的集合,一个程序就是一个进程。

线程:一个程序最小的运行单位。

import threading #引入线程模块

importtimedefrun():

time.sleep(5)print('over')

start_time=time.time()

run()

run()

run()

run()

end_time=time.time()print('run_time', end_time-start_time)#结果:run_time=20.38954234234

上面这个例子是单线程执行的,运行时间是20多秒,如果使用多线程,则会并行执行,执行结果应该是5秒左右。

主线程等待子线程

方法一:想要让主线程等待添加的线程,需要先把创建的线程统一放到list里面,循环执行完,使用.join()方法,如下:

importthreadingimporttimedefrun():

time.sleep(5)print('over')

start_time=time.time()

thread_list=[]for i in range(5):

t=threading.Thread(target=run) #实例化一个线程,target代表要指定执行什么,例如:target=run就是执行run()这个函数,指定函数名,不要加()

#函数里面如果有参数,可以加上args,如:t=threading.Thread(target=run,args[1,2,3,]) ,有几个参数,就在[]里面写几个参数

t.start() #启动这个线程

thread_list.append(t) #把线程放到list里面for thread inthread_list:

thread.join()#主线程等待子线程

end_time=time.time()print('run_time=', end_time-start_time)

方法二:每一次循环都会判断下剩余的线程是不是只剩下1个,不是一个的话,继续循环,直到剩下的线程数为1时,继续往下执行,如下:

importthreadingimporttime

threads=[]

start_time2=time.time()definsert_db():

time.sleep(3)print('insert_db over')for i in range(3):

t= threading.Thread(target=insert_db)

t.start()while threading.activeCount()!=1:#每一次循环都会判断下剩余的线程是不是只剩下1个,不是一个的话,继续循环,直到剩下的线程数为1时,继续往下执行

passend_time2=time.time()print('多线程执行的时间',end_time2 -start_time2)print('锁门...')

线程 单个启动 & 同时启动

importthreading'''单个启动'''

for i in range(10):

t= threading.Thread(target=func,args=[1])

t.start()#start()在这里是一个一个线程拿过来,单个启动

'''同时启动启动'''ts=[]for i in range(10):

t= threading.Thread(target=func,args=[1])

ts.append(t)#先10个线程放到一个list里面

for t ints:

t.start()#循环同时启动

练习分析:

importthreadingimporttimedefinsert_db():

time.sleep(3)print('insert_db over')

start_time=time.time()for i in range(3):

t= threading.Thread(target=insert_db)

t.start()

end_time=time.time()print('多线程执行的时间',end_time -start_time)

结果:

多线程执行的时间0.0010001659393310547insert_db over

insert_db over

insert_db over

我们看到的结果是中运行时间是0.0010001659393310547,还不到一秒钟,而我们代码所设置的是最少也会需要3秒钟,为什么时间会是这么短呢?

——因为所得到的时间只是主线程执行完它的工作时间,并不包括子线程执行的额时间。

压测练习:

importthreadingimportrequestsdefrequest():whileTrue:

r= requests.get('http://api.nnzhp.cn/api/user/stu_info?stu_name=%E7%8E%8B%E5%B0%8F%E6%9C%88')print(r.json())for i in range(10):

t= threading.Thread(target=request)

t.start()

运行后会一直死循环,一直压测,可以加入压测的时间

单线程下载网页:

importrequests,time,threadingfrom hashlib importmd5

result_list={}defdown_load_pic(url):

req=requests.get(url)

m= md5(url.encode()) #把url转换为二进制md5下

file_name = m.hexdigest()+'.png' #拼接文件名

with open(file_name ,'wb') as fw:

fw.write(req.content)#return file_name

result_list[file_name] =threading.current_thread()

url_list= ['http://www.nnzhp.cn/wp-content/uploads/2019/10/f410afea8b23fa401505a1449a41a133.png','http://www.nnzhp.cn/wp-content/uploads/2019/11/481b5135e75c764b32b224c5650a8df5.png','http://www.nnzhp.cn/wp-content/uploads/2019/11/b23755cdea210cfec903333c5cce6895.png','http://www.nnzhp.cn/wp-content/uploads/2019/11/542824dde1dbd29ec61ad5ea867ef245.png']

start_time=time.time()for url inurl_list:

down_load_pic(url)

end_time=time.time()print(end_time -start_time)#结果:4.439253807067871

多线程下载网页:

importthreadingimportrequestsimporthashlibimporttimedefdown_load(url):

name= hashlib.md5(url.encode()).hexdigest() #把url使用md5转化为密文

r =requests.get(url)

with open('%s.jpg'%name,'wb') as fw:

fw.write(r.content)

l=['http://www.nnzhp.cn/wp-content/themes/QQ/images/logo.jpg','http://www.nnzhp.cn/wp-content/uploads/2016/12/2016aj5kn45fjk5-150x150.jpg','http://www.nnzhp.cn/wp-content/themes/QQ/images/thumbnail.png']for i inl:

t= threading.Thread(target=down_load,args=[i])#参数只有一个时,要使用()的方式来写,需要多加一个逗号,如:args=(i,)

#args是存参数的,如果里面只有一个参数的话,一定要在这个参数后面加一个逗号,因为是保存在元组里,如果不加逗号,它会默认为是字符串 应该写成:args=(url,)

t.start()while threading.activeCount()!=1:pass

print('down load over...')

一个进程里面至少有一个线程,这个线程就是主线程。

主线程只是调度用的,它把子线程招来之后就完事了,因此如果要统计运行时间,必须要让主线程等待所有的子线程都执行完后再记录结束时间。

case_result =[]defrun_case(case_name):print('run case..',case_name)

case_result.append( {case_name:'success'})#多线程运行函数时,函数的返回值是拿不到的,所以定义一个list#把函数运行的结果都存进去就ok了

查看当前有多少个线程:threading.activeCount()

import threading #引入线程模块

import time

def run():

time.sleep(1)for i in range(10):

t= threading.Thread(target=run)

t.start()

print('当前有%s个线程'%threading.activeCount()) #当前有几个线程

结果:当前有11个线程

为什么会是11个线程而不是10个线程呢?我们明明是循环启动了10个线程,结果怎么就是11个了呢?

因为本身有一个主线程在运行,好比是我们自己在干活,我们又找了10个人来一起干活,加上我们自己一共是11个人在干活。所有的程序默认就会有一个线程。

2、守护线程

一旦主线程死掉,那么守护线程不管有没执行完事,全部结束,相当于你是一个国王(主线程),你有很多仆人(守护线程),仆人都是为你服务的,一旦你死了,那么你的仆人都需要给你陪葬。

importthreadingimporttimedeftalk(name):print('正在和%s聊天'%name)

time.sleep(200)print("qq主窗口")

t= threading.Thread(target=talk,args=['刘一'])

t.setDaemon(True)#设置线程为守护线程

t.start()

time.sleep(5)print('结束。。。')

结果:

qq主窗口

正在和刘一聊天

结束。。。

如果是没有设置为守护线程,本身程序执行需要有200秒才可以结束,设置了守护线程后,主线程5秒到了就会结束,主线程结束了,剩下的正在执行的线程已经是守护线程了,不会继续200秒结束,会立即跟随主线程结束。

3、线程锁

多个线程同时在操作同一个数据的时候,会有问题,就要把这个数据加个锁,然后同一时间只能有一个线程操作这个数据了【多个人或是多线程在同时操作同一个数据时,可能会有问题,可以加下锁】

忘记了解锁或是代码运行时没有处理异常而没有走到解锁那里,都会成为线程死锁。

举例理解:比如说我们家里的卫生间,男女共用的,如果你进去时,没有锁门,有可能会有其他的开门,所以你需要进去时把门锁一下,不用了,再把锁打开下。

importthreadingfrom threading importLock

num=0

lock= Lock()#申请一把锁

defrun():globalnum

lock.acquire(timeout = 3)#加锁,timeout是指定下时间,可以不写,如果指定了时间,超过后申请的锁就会失效

num+=1lock.release()#解锁

lis=[]for i in range(5):

t= threading.Thread(target=run)

t.start()

lis.append(t)for t inlis:

t.join()print('over',num)

#多个线程操作同一个数据的时候,就得加锁

importthreading

num=0

lock= threading.Lock() #申请一把锁,实例化一把锁

defadd():globalnum#lock.acquire()#加锁

#num+=1

#lock.release()#解锁 #不解锁,就会产生线程死锁,会一直在等待

with lock:#简写,用with也会帮你加锁,解锁

num+=1

for i in range(20):

t= threading.Thread(target=add,)

t.start()while threading.activeCount() !=1:pass

查看当前哪个线程在执行:threading.current_thread()

importthreading

count=0

lock=threading.Lock()deftest():globalcountprint(threading.current_thread())

lock.acquire()#加锁

count+=1lock.release()#解锁

#线程死锁

for i in range(3):

t= threading.Thread(target=test)

t.start()

结果:

下面来个简单的爬虫,看下多线程的效果:

importthreadingimportrequests, time

urls={"baidu": 'http://www.baidu.com',"blog": 'http://www.nnzhp.cn',"besttest": 'http://www.besttest.cn',"taobao": "http://www.taobao.com","jd": "http://www.jd.com",

}defrun(name, url):

res=requests.get(url)

with open(name+ '.html', 'w', encoding=res.encoding) as fw:

fw.write(res.text)

start_time=time.time()

lis=[]for url inurls:

t= threading.Thread(target=run, args=(url, urls[url]))

t.start()

lis.append(t)for t inlis:

t.join()

end_time=time.time()print('run time is %s' % (end_time -start_time))#下面是单线程的执行时间

#start_time = time.time()

#for url in urls:

#run(url,urls[url])

#end_time = time.time()

#print('run time is %s'%(end_time-start_time))

4、多进程,上面说了Python里面的多线程,是不能利用多核CPU的,如果想利用多核CPU的话,就得使用多进程,python中多进程使用multiprocessing模块。

一个电脑有几核的CPU,就只能同时运行几个任务

——比如说我们的电脑是4核的CPU,只可以同时运行4个进程,但我们在实际使用中,如果是4核的CPU,运行的并不止是4个程序,这是因为CPU上下文切换的,这个程序运行完了,

会立即切换到另外一个运行,我们感觉不到。

importmultiprocessing,time

lock= multiprocessing.Lock() #申请一把锁

a = 1

defdown_load():

time.sleep(3)globala

with lock:#使用with这个方法会自动的去判断使用完了自动的关掉锁释放【with是自动管理上下文的,操作文件时也可以使用,会自动识别关闭】

a+=1

print("运行完了")if __name__ == '__main__':for i in range(5):

p= multiprocessing.Process(target=down_load)#p=multiprocessing.Process(target=down_load,args =[1,2]) #有参数了可以加上args

p.start()while len(multiprocessing.active_children())!=0:#等待子进程结束

pass

print(multiprocessing.current_process())print('end')

5、进程池

还可以使用进程池来快速启动几个进程,使用进程池的好处的就是他会自动管理进程数,咱们只需要给他设置一个最大的数就ok了。有新的请求提交到Pool中时,如果池还没有满,那么就会创建一个新的进程用来执行该请求;但如果池中的进程数已经达到指定的最大值,那么该请求就会等待,直到池中有进程结束,才会用之前的进程来执行新的任务。【数据大时使用】

from multiprocessing importPoolimportosdefworker(msg):print("%s开始执行,进程号为%d" %(msg,os.getpid()))if __name__ == '__main__':

po= Pool(3) #定义一个进程池,最大进程数3

for i in range(0, 10):#Pool().apply_async(要调用的目标,(传递给目标的参数元祖,))

#每次循环将会用空闲出来的子进程去调用目标

po.apply_async(func=worker,args=(i,))#第一个func参数指定运行的函数,第二个args是参数,没有参数可以不写

print("----start----")

po.close()#关闭进程池,关闭后po不再接收新的请求

po.join() #等待po中所有子进程执行完成,必须放在close语句之后

print("-----end-----")

总结:

线程是用来干活的,只有进程的话是没办法运行的,进程里其实是线程在具体干活的。

线程和线程之间是互相独立的。

线程是在进程里面

默认,一个进程里面只有一个进程

进程相当于是一个工厂,线程相当于是工厂里面具体干活的一个人

主线程,也就是程序一开始运行的时候,最初的那个线程

子线程,通过thread类实例化的线程,都是子线程

主线程等待子线程,执行结束后,主线程再去做别的操作

主线程执行完它自己工作的时间,并不包括子线程执行的时间

Python里面的多线程利用不了多核的cpu,因为如果是在多个CPU上运行,运行的结果会不太一样,所以加入一个全局解释器锁(GLI),保证线程都在同一个cpu运行

多进程可以利用多核CPU

CPU密集型任务,用多进程  ->消耗的CPU比较多

IO(磁盘IO、网络IO)密集型任务,用多线程   ->消耗IO比较多【IO是上传、下载的意思】

多线程,线程之间的的数据是共享的

多进程,每个进程之间的数据是独立的,正是因为每个进程都是独立的,所以多进程里面加锁是没任何意义的。

扩展:

协程,只有有个线程在运行,每遇到消耗IO的地方就会立马切换,在切换到另一个,等着这个IO结束了,再去拿到数据,性能比较好;如:nginx就是使用的协程。

任何付出都是值得的,会越来越好

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值