1.线程
线程,有时被称为轻量进程(Lightweight Process,LWP),是程序执行流的最小单元。一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。由于线程之间的相互制约,致使线程在运行中呈现出间断性。线程也有就绪、阻塞和运行三种基本状态。就绪状态是指线程具备运行的所有条件,逻辑上可以运行,在等待处理机;运行状态是指线程占有处理机正在运行;阻塞状态是指线程在等待一个事件(如某个信号量),逻辑上不可执行。每一个程序都至少有一个线程,若程序只有一个线程,那就是程序本身。
线程是程序中一个单一的顺序控制流程。进程内有一个相对独立的、可调度的执行单元,是系统独立调度和分派CPU的基本单位指令运行时的程序的调度单位。在单个程序中同时运行多个线程完成不同的工作,称为多线程。
2.创建线程
import _thread
import threading
import time
def job():
print("这是一个需要执行的任务")
# 激活的线程个数
print("当前线程的个数:", threading.active_count())
# 打印当前线程的详细信息
print("当前线程信息:", threading.current_thread())
time.sleep(100)
if __name__ == "__main__":
job()
3.thread模块中创建多线程
import _thread
import threading
import time
def job(name):
print("这是一个需要执行的任务")
# # 激活的线程个数
# print("当前线程的个数:", threading.active_count())
# # 打印当前线程的详细信息
# print("当前线程信息:", threading.current_thread())
print(name, time.ctime())
time.sleep(2)
if __name__ == "__main__":
# 创建多个线程, 但是没有开始执行任务;
_thread.start_new_thread(job,('thread1', ))
_thread.start_new_thread(job,('thread2', ))
while True:
pass
import _thread
import threading
import time
def job(name):
print("这是一个需要执行的任务: %s" %(name))
# 激活的线程个数
print("当前线程的个数:", threading.active_count())
# 打印当前线程的详细信息
print("当前线程信息:", threading.current_thread())
time.sleep(100)
print(name, time.ctime())
if __name__ == "__main__":
job('job0')
# 创建多个线程
t1 = threading.Thread(target=job, name='job1', args=("job1-name",))
t1.start()
t2 = threading.Thread(target=job, name='job2', args=("job2-name",))
t2.start()
print('hello')
4.不使用多线程
import time
def music(name):
for i in range(2):
print("正在听音乐%s" %(name))
time.sleep(1)
def code(name):
for i in range(2):
print("正在编写代码%s" %(name))
time.sleep(2)
if __name__ == '__main__':
start_time = time.time()
music("中国梦")
code("爬虫")
print("花费时间: %s" %(time.time()-start_time))
5.多线程的join方法
import threading
import time
def music(name):
for i in range(2):
print("正在听音乐%s" %(name))
time.sleep(1)
def code(name):
for i in range(2):
print("正在编写代码%s" %(name))
time.sleep(2)
if __name__ == '__main__':
start_time = time.time()
# music("中国梦")
# code("爬虫")
t1 = threading.Thread(target=music, args=("中国梦",))
t2 = threading.Thread(target=code, args=("爬虫", ))
t1.start()
t2.start()
# 等待所有的子线程执行结束之后, 继续执行主线程的内容;
t1.join()
t2.join()
print("花费时间: %s" %(time.time()-start_time))
5.threading的set_daemon方法实现
# 当主线程执行结束, 让没有执行的线程强制结束;set_daemon
import threading
import time
# 任务1:
def music(name):
for i in range(2):
print("正在听音乐%s" %(name))
time.sleep(1)
# 任务2:
def code(name):
for i in range(2):
print("正在编写代码%s" %(name))
time.sleep(2)
if __name__ == '__main__':
start_time = time.time()
# music("中国梦")
# code("爬虫")
t1 = threading.Thread(target=music, args=("中国梦",))
t2 = threading.Thread(target=code, args=("爬虫", ))
# 将t1线程生命为守护线程, 如果设置为True, 子线程启动, 当主线程执行结束, 子线程也结束
# 设置setDaemon必须在启动线程之前进行设置;
t1.setDaemon(True)
t2.setDaemon(True)
t1.start()
t2.start()
# 等待所有的子线程执行结束之后, 继续执行主线程的内容;
# t1.join()
# t2.join()
print("花费时间: %s" %(time.time()-start_time))
6.多线程paramiko执行命令
# 基于ssh用于连接远程服务器做操作:远程执行命令, 上传文件, 下载文件
import threading
import paramiko
from paramiko.ssh_exception import NoValidConnectionsError, AuthenticationException
def connect(cmd, hostname, port=22, user='root'):
# ssh root@172.25.254.250
# 创建一个ssh对象;
client = paramiko.SSHClient()
# 返回一个私钥对象
private_key = paramiko.RSAKey.from_private_key_file('id_rsa')
# 2. 解决问题:如果之前没有;连接过的ip, 会出现
# Are you sure you want to continue connecting (yes/no)? yes
# 自动选择yes
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
try:
# 3. 连接服务器
client.connect(hostname=hostname,
port=port,
username=user,
pkey=private_key
)
# 4. 执行操作
stdin, stdout, stderr = client.exec_command(cmd)
except NoValidConnectionsError as e:
print("%s连接失败" %(hostname))
except AuthenticationException as e:
print("%s密码错误" %(hostname))
else:
# 5. 获取命令的执行结果;
result = stdout.read().decode('utf-8')
print("%s运行结果:" %(hostname), result)
finally:
# 6. 关闭连接
client.close()
# for count in range(254):
# host = '172.25.254.%s' %(count+1)
# print(host.center(50, '*'))
# connect('uname', host)
# 用来存储创建的所有线程对象;
threads = []
for count in range(254):
host = '172.25.254.%s' %(count+1)
# print(host.center(50, '*'))
t = threading.Thread(target=connect, args=('uname', host))
threads.append(t)
t.start()
# join方法, 等待所有的子线程执行结束;
_ = [thread.join() for thread in threads]
print("任务执行结束........")
7.多线程应用获取IP地理位置
import json
import threading
from urllib.request import urlopen
import time
def job(ip):
"""获取指定ip对应的地理位置"""
url = "http://ip.taobao.com/service/getIpInfo.php?ip=%s" % (ip)
# 根据url获取网页的内容, 并且解码为utf-8格式, 识别中文;
text = urlopen(url).read().decode('utf-8')
# 将获取的字符串类型转换为字典, 方便处理
d = json.loads(text)['data']
country = d['country']
city = d['city']
print("%s:" %(ip), country, city)
def has_many_thread():
start_time = time.time()
threads = []
ips = ['172.25.254.250', '8.8.8.8',
'172.25.254.250', '8.8.8.8',
'172.25.254.250', '8.8.8.8' ]
for ip in ips:
# 实例化线程对象
t = threading.Thread(target=job, args=(ip, ))
threads.append(t)
# 启动线程执行任务
t.start()
# join方法
[thread.join() for thread in threads]
print("Success, 使用多线程运行时间为:%s" %(time.time()-start_time))
def has_no_thread():
start_time = time.time()
ips = ['172.25.254.250', '8.8.8.8',
'172.25.254.250', '8.8.8.8',
'172.25.254.250', '8.8.8.8']
for ip in ips:
job(ip)
print("Success, 未使用多线程运行时间为:%s" % (time.time() - start_time))
if __name__ == '__main__':
has_many_thread()
has_no_thread()
8.join的详细理解
import threading
import time
def job():
time.sleep(1)
print('job')
# t1 = threading.Thread(target=job)
# t1.start()
# t1.join() # 会等待, 直到t1线程执行结束;阻塞正在调用的线程
# t2 = threading.Thread(target=job)
# t2.start()
# t2.join() # 会等待, 直到t2线程执行结束;阻塞正在调用的线程
t1 = threading.Thread(target=job)
t1.start()
t2 = threading.Thread(target=job)
t2.start()
t1.join()
t2.join()
print('main thread end')
9.threading创建线程方法2
import threading
# 类的继承
class IpThread(threading.Thread):
# 重写构造方法;
def __init__(self, jobname):
super(IpThread, self).__init__()
self.jobname = jobname
# 将多线程需要执行的任务重写到run方法中;
def run(self):
print("this is a job")
t1 = IpThread(jobname="new job")
t1.start()
import json
import threading
# 类的继承
from urllib.error import HTTPError
from urllib.request import urlopen
import time
class IpThread(threading.Thread):
# 重写构造方法;如果执行的任务需要传递参数, 那将参数通过构造函数与self绑定;
def __init__(self, jobname, ip):
super(IpThread, self).__init__()
self.jobname = jobname
self.ip = ip
# 将多线程需要执行的任务重写到run方法中;
def run(self):
try:
# 需要有一个参数, 传ip;
url = "http://ip.taobao.com/service/getIpInfo.php?ip=%s" % (self.ip)
# 根据url获取网页的内容, 并且解码为utf-8格式, 识别中文;
text = urlopen(url).read().decode('utf-8')
except HTTPError as e:
print("Error: %s获取地理位置网络错误" %(self.ip))
else:
# 将获取的字符串类型转换为字典, 方便处理
d = json.loads(text)['data']
country = d['country']
city = d['city']
print("%s:" % (self.ip), country, city)
def use_thread():
start_time = time.time()
ips = ['172.25.254.250', '8.8.8.8',
'172.25.254.250', '8.8.8.8',
'172.25.254.250', '8.8.8.8']
threads = []
for ip in ips:
t = IpThread(jobname="爬虫", ip=ip)
threads.append(t)
t.start()
# 等待所有的子线程执行结束
[thread.join() for thread in threads]
print("Success, 运行时间为%s" % (time.time() - start_time))
if __name__ == "__main__":
use_thread()
10.线程同步之线程锁
import threading
def add(lock):
# 2. 操作变量之前进行加锁
lock.acquire()
global money
for i in range(1000000):
money += 1
# 3. 操作变量完成后进行解锁
lock.release()
def reduce(lock):
# 2. 操作变量之前进行加锁
lock.acquire()
global money
for i in range(1000000):
money -= 1
# 3. 操作变量完成后进行解锁
lock.release()
if __name__ == '__main__':
money = 0
# 1. 实例化一个锁对象
lock = threading.Lock()
t1 = threading.Thread(target=add, args=(lock, ))
t2 = threading.Thread(target=reduce, args=(lock, ))
t1.start()
t2.start()
# 等待所有子线程执行结束
t1.join()
t2.join()
print("最终金额为:%s" %(money))
11.GIL全局解释器锁
python使用多线程 , 是个好主意么? 为什么?
- GIL(全局解释器锁)
- python解释器默认每次只允许一个线程执行
执行过程:
1). 设置GIL
2). 切换到线程去运行对应的任务;
3). 运行
- 执行完了
- time.sleep()
- 获取其他信息才能继续执行, eg: 从网络上获取网页信息等;
3. 把线程设置为睡眠状态
4. 解锁GIL
5.再次重复执行上述内容;
python解释器:Cpython解释器, Jpython解释器, p-python解释器
方法的选择:
Python并不支持真正意义上的多线程。Python中提供了多线程包,但是如果你想通过多线程提高代码的速度,
使用多线程包并不是个好主意。Python中有一个被称为Global Interpreter Lock(GIL)的东西,
它会确保任何时候你的多个线程中,只有一个被执行。线程的执行速度非常之快,会让你误以为线程是并行执行的,
但是实际上都是轮流执行。经过GIL这一道关卡处理,会增加执行的开销。这意味着,如果你想提高代码的运行速度,
使用threading包并不是一个很好的方法。
# I/O密集型操作: 多线程操作
# CPU/计算密集型:
import threading
from mytimeit import timeit
def job(l):
sum(l)
@timeit
def use_thread():
li = range(1,10000)
for i in range(5):
t = threading.Thread(target=job, args=(li, ))
t.start()
@timeit
def use_no_thread():
li = range(1, 10000)
for i in range(5):
job(li)
if __name__ == "__main__":
use_thread()
use_no_thread()
12.线程池
线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是后台线程。每个线程都使用默认的堆栈大小,以默认的优先级运行,并处于多线程单元中。如果某个线程在托管代码中空闲(如正在等待某个事件),则线程池将插入另一个辅助线程来使所有处理器保持繁忙。如果所有线程池线程都始终保持繁忙,但队列中包含挂起的工作,则线程池将在一段时间后创建另一个辅助线程但线程的数目永远不会超过最大值。超过最大值的线程可以排队,但他们要等到其他线程完成后才启动。
# 注意: python3.2版本以后才可以使用;
from concurrent.futures import ThreadPoolExecutor
import time
# 需要执行的任务
def job():
print("this is a job")
return "hello"
if __name__ == '__main__':
# 实例化对象, 线程池包含10个线程来处理任务;
pool = ThreadPoolExecutor(max_workers=10)
# 往线程池里面扔需要执行的任务, 返回一个对象,( _base.Future实例化出来的)
f1 = pool.submit(job)
f2 = pool.submit(job)
# 判断任务是否执行结束
print(f1.done())
time.sleep(1)
print(f2.done())
# 获取任务执行的结果
print(f1.result())
print(f2.result())
13.线程池执行多个循环执行任务多次
# 基于ssh用于连接远程服务器做操作:远程执行命令, 上传文件, 下载文件
import threading
import paramiko
from paramiko.ssh_exception import NoValidConnectionsError, AuthenticationException
def connect(cmd, hostname, port=22, user='root'):
# ssh root@172.25.254.250
# 创建一个ssh对象;
client = paramiko.SSHClient()
# 返回一个私钥对象
private_key = paramiko.RSAKey.from_private_key_file('id_rsa')
# 2. 解决问题:如果之前没有;连接过的ip, 会出现
# Are you sure you want to continue connecting (yes/no)? yes
# 自动选择yes
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
try:
# 3. 连接服务器
client.connect(hostname=hostname,
port=port,
username=user,
pkey=private_key
)
# 4. 执行操作
stdin, stdout, stderr = client.exec_command(cmd)
except NoValidConnectionsError as e:
print("%s连接失败" %(hostname))
except AuthenticationException as e:
print("%s密码错误" %(hostname))
else:
# 5. 获取命令的执行结果;
result = stdout.read().decode('utf-8')
print("%s运行结果:" %(hostname), result)
finally:
# 6. 关闭连接
client.close()
# for count in range(254):
# host = '172.25.254.%s' %(count+1)
# print(host.center(50, '*'))
# connect('uname', host)
# ***********************方法2: 线程池只有50个线程处理所有的任务****************
# 创建线程池对象
pool = ThreadPoolExecutor(max_workers=50)
# 依次向线程池提交任务
for count in range(254):
host = '172.25.254.%s' % (count + 1)
pool.submit(connect, 'uname', host)
# ******************方法1: 每个ip使用一个线程来处理********************
# # 用来存储创建的所有线程对象;
# threads = []
# for count in range(254):
# host = '172.25.254.%s' %(count+1)
# # print(host.center(50, '*'))
# t = threading.Thread(target=connect, args=('uname', host))
# threads.append(t)
# t.start()
#
#
# # join方法, 等待所有的子线程执行结束;
# _ = [thread.join() for thread in threads]
#
#
# print("任务执行结束........")
14.线程池与map函数
from urllib.error import HTTPError
from urllib.request import urlopen
from concurrent.futures import ThreadPoolExecutor
from concurrent.futures import as_completed
import time
URLS = ['http://httpbin.org', 'http://example.com/',
'https://api.github.com/'] * 10
def get_page(url, timeout=3):
try:
content = urlopen(url).read()
return {'url':url, 'len':len(content)}
except HTTPError as e:
return {'url':url, 'len':0}
# 方法1: submit提交任务
# start_time = time.time()
# pool = ThreadPoolExecutor(max_workers=20)
# futuresObj = [pool.submit(get_page, url) for url in URLS]
#
# # 注意: 传递的时包含futures对象的序列, as_complete, 返回已经执行完任务的future对象,
# # 直到所有的future对应的任务执行完成, 循环结束;
# # for finish_fs in as_completed(futuresObj):
# # print(finish_fs.result() )
#
# for future in futuresObj:
# print(future.result())
#
# print("执行时间:%s" %(time.time()-start_time))
#
# 方法2:通过map方式执行
pool = ThreadPoolExecutor(max_workers=20)
for res in pool.map(get_page, URLS):
print(res)