多线程

多线程
1、线程是程序里面最小的执行单元。
2、进程是资源的集合

多线程就是多个线程一起干活,及并行运行
1、传统方式:串行,循环5次需要15s
import threading
def run():
    time.sleep(3)#干活需要3秒
    print('哈哈哈哈哈')
for i in range(5):#串行,就是一个一个输出
    run()
#运行的时候可以看到每个三秒输出:哈哈哈哈哈

2、并行:利用多线程并发,节省时间

import  threading,time
def run():
    time.sleep(3)#干活需要3秒
    print('哈哈哈哈哈')
for i in range(5):#并行
    t=threading.Thread(target=run)#实例化一个线程,其中target=执行的函数名
  t.start() # #多线程,就是N个线程一起在干活 #结果,运行后三秒输出5个结果:哈哈哈哈哈哈

 例子:多线程爬虫,比较串行和并发的时间:

(1)串行方式:

import threading,time,requests
urls = {
    'besttest':'http://www.besttest.cn',
    'niuniu':'http://www.nnzhp.cn',
    'dsx':'http://www.imdsx.cn',
    'cc':'http://www.cc-na.cn'}
data={}#在线程中是获取不到返回值的,只能通过定义一个列表或者字典,将数据保存在列表或者字典中返回才能获取
def down_html(filename,url):
    sart_time = time.time()#开始时间
    res=requests.get(url).content
    open(filename+'.html','wb').write(res)
    end_time = time.time()#结束时间
    run_time = end_time - sart_time
    data[url]=run_time#运行时间
    print(run_time,url)#打印每个下载文件的运行时间

#1、用串行
sart_time=time.time()#开始时间
for k,v in urls.items():
    down_html(k,v)
end_time=time.time()#结束时间
run_time=end_time-sart_time#运行时间
print('串行下载总共花了XXX时间',run_time)

#结果
0.33101892471313477 http://www.besttest.cn
1.4620835781097412 http://www.nnzhp.cn
0.9180524349212646 http://www.imdsx.cn
3.6742103099823 http://www.cc-na.cn
串行下载总共花了XXX时间 6.38536524772644

2、并行方式:

(1)一开始以为像串行一样直接在循环线程之前加开始时间,循环线程结束后加结束时间就能够计算到并行的运行时间,其实这样是错误的,究竟是为什么呢?看下面分析:

因为在运行多线程时候,默认先会运行主线程,其他线程只是开始运行(下载网页)并不一定结束(而一个进程中的多个线程都是相互独立的。)主线程负责定义函数,启动其

他线程等。因此,最终并行运行的结果时间,仅仅是主线程的运行时间。

import threading,time,requests

urls = {
    'besttest':'http://www.besttest.cn',
    'niuniu':'http://www.nnzhp.cn',
    'dsx':'http://www.imdsx.cn',
    'cc':'http://www.cc-na.cn'}

def down_html(file_name,url):
    res = requests.get(url).content  #返回二进制内容
    open(file_name+'.html','wb').write(res)  #因此打开的时候,用wb二进制打开

#用并行方式
start_time = time.time()
for k,v in urls.items():#因为urls里有3个Key,因此会启动3个线程
    t = threading.Thread(target=down_html,args=(k,v))  #使用多线程调用参数时,必须用args=xx才能传参
    t.start()
end_time = time.time()
run_time = end_time - start_time
print('下载共花了%s时间'%run_time)

#结果:
下载共花了0.004000425338745117时间

 (2)那如何计算多线程的运行时间呢?接下来先讲一下线程等待:

1、计算主线程时间,跟上述代码一样

import  threading,time
#主线程:默认有个主线程,主线程主要是启动子线程,启动之后跟子线程没有关系
#上面计算的是主线程运行的时间,不是子线程的时间
def run():
    time.sleep(3)
    print('哈哈哈哈')
#1、主线程时间
sart_time = time.time()
for i in range(5):  # 并行
    t=threading.Thread(target=run)#实例化一个线程
    t.start()
end_time=time.time()
run_time=end_time-sart_time
print('run_time',run_time)

2、可以使用join()函数设置线程等待:

t.join()放在线程循环体里面,主线程等待第一个线程结束才开始循环第二个线程,所以并没有进行并行,还是跟串行一样

import  threading,time
def run():
    time.sleep(3)
    print('哈哈哈哈')

sart_time = time.time()
for i in range(5):  # 并行
    t=threading.Thread(target=run)#实例化一个线程
    t.start()
    t.join()#来一个线程等待一个线程执行结束,等执行结束了才循环继续下一个线程执行结束,所以总共会等待15s,变成串行
end_time=time.time()
run_time=end_time-sart_time
print('run_time',run_time)

#结果
哈哈哈哈
哈哈哈哈
哈哈哈哈
哈哈哈哈
哈哈哈哈
run_time 15.000857830047607

3、主线程等待5个线程完成:这就是多线程并行时间

主线程负责启动5个子线程,把每个线程放在threads list里,然后等待所有线程等待完毕后,再执行end_time = time.time()语句,实现最后计算所有线程都结束的并发时间。

import  threading,time
def run():
    time.sleep(3)
    print('哈哈哈哈')
sart_time = time.time()
threads=[]
for i in range(5):  # 并行
    t=threading.Thread(target=run)#实例化一个线程
    t.start()
    threads.append(t)
for t in threads:#为什么循环,是因为循坏等待才能计算子线程的运行时间
    t.join()#循环等待,只主线程循坏等待5个子线程执行结束,及等5个线程执行完就行了
end_time=time.time()
run_time=end_time-sart_time
print('run_time',run_time)
#结果
哈哈哈哈
哈哈哈哈
哈哈哈哈
哈哈哈哈
哈哈哈哈
run_time 3.001171827316284

4、计算上述爬虫的多线程执行时间,代码如下:

import requests
data={}#在线程中是获取不到返回值的,只能通过定义一个列表或者字典,将数据保存在列表或者字典中返回才能获取

def down_html(filename,url):#计算每个线程的执行时间
    sart_time = time.time()
    res=requests.get(url).content
    open(filename+'.html','wb').write(res)
    end_time = time.time()
    run_time = end_time - sart_time
    data[url]=run_time
    print(run_time,url)

#用并行
sart_time=time.time()
threads=[]
for k,v in urls.items():#5次
    t=threading.Thread(target=down_html,args=(k,v))#多线程的函数如果传参数的话,必须用args
    t.start()
    threads.append(t)
for t in threads:#为什么循环,是因为循坏等待才能计算子线程的运行时间
    t.join()
end_time=time.time()
run_time=end_time-sart_time
print('并行下载总共花了XXX时间',run_time)
#6个线程,进程里面默认有一个线程,这个线程叫做主线程

#结果:
0.37302136421203613 http://www.besttest.cn
1.2510716915130615 http://www.imdsx.cn
1.5230872631072998 http://www.nnzhp.cn
3.583204746246338 http://www.cc-na.cn
并行下载总共花了XXX时间 3.588205337524414

 线程锁

import threading,time
num=1
lock=threading.Lock()#申请一把锁
def run():
    time.sleep(1)
    global num
    lock.acquire()#加锁
    num+=1
    lock.release()#解锁
ts=[]
for i in range(50):
    t=threading.Thread(target=run)
    t.start()
    ts.append(t)
[t.join() for t in ts]
print(num)
#锁的就是,在多个线程同时修改一个数据的时候,可能会把数据覆盖,在Python2里面需要加锁
#Python3中里面不加锁也无所谓,默认会自动帮你加锁
原文地址:https://www.cnblogs.com/hwtfamily/p/9095176.html