一、概念
进程的概念:运行着的程序
线程的概念:
1.每个进程里面至少包含一个线程
2.线程是操作系统创建的,用来控制代码执行的数据结构
3.线程就像代码的执行许可证
4.单线程程序,主线程的入口就是代码的开头
5.主线程顺序往下执行,直到所有的代码都执行完
概念对应(银行办理业务):
一个服务窗口 = cpu的一个核
客户 = 进程(运行着的程序)
调度员 = 操作系统(os)
服务号 = 线程
一个服务号,相当于银行要办的一件事情,对应线程,计算机程序要做的一件事情,也就是一段代码要执行
调度员分配服务号给客户 = os分配线程给进程代码
服务窗口给客户办业务 = cpu核心执行线程代码
注意点:
服务窗口,在一个时间点上只能服务一个顾客业务
cpu核心,在一个时间点上只能执行一个线程代码
·
调度的概念:
1.调度员分配窗口给客户
2.一个客户不一定占用一个窗口一直到它结束
1)比如需要很长时间填写表格
2)这时候可以让下一个客户来办理
3)先前的客户填好了表格,再继续
操作系统不会让一个线程一直占有cpu的
线程库:
1.代码通过系统调用,请求os分配一个新的线程
2.python里面:
1)thread库
2)threading库
3)都可以用来创建和管理线程
4)thread 比较低层
5)threading是thread模块的扩展,提供了很多线程同步功能,使用起来更加方便强大
多线程的概念:
代码通过系统调用,请求os分配一个新的线程,与原来的线程并行的执行一段代码
我们为什么需要多线程:
1.我们的大脑有时候需要同时处理多个事情,如我们在酒席上可能会一边和朋友聊天,一边思考着各种其他事情
2.多线程给一个程序并行执行代码的能力,同时处理多个任务
3.常见的:ui线程,任务线程 task exeute
多线程使用共享数据:
1.从例子说起,高铁上的厕所,某个时刻只能一个人使用,进入后往往立即锁门(表示已经被使用),看到的人,门口排队等待,用完开锁(表示已经使用完了),排队的人中下一个去使用(重复这个过程)
2.有些资源是某个时刻独占使用的,如果不加锁,某人使用厕所,另一个人也进入使用,发生冲突;锁保证了,只有一个人使用,别人必须等待
加锁:
1.注意锁的代码位置
2.在访问共享对象的代码前,要调用lock对象的acquire方法,进行上锁操作。当多个线程同时执行lock.acquire()时,只有一个线程能成功地获取锁,然后继续执行代码,其他线程就继续等待直到获得锁为止。
3.访问结束后,一定要调用lock对象的release方法,进行解锁操作。否则其它等待锁的线程将永远等待下去,成为死线程。
4.加锁是原子操作,不会有同时加锁的情况
条件变量:
1.生产者、消费者
1)一个线程负责让用户输入命令,存入一个list中
2)另一个线程负责从list中取出命令,执行命令
3)此时,list是共享数据,应该用锁,但用户输入命令的速度,和执行命令的速度,谁快谁慢,还很难说,消费者依赖生产者才能工作
2.负责让用户输入命令的线程:生产者,产生命令存入列表中
3.负责执行命令的线程:消费者,取出列表中的命令
4.条件变量的使用:
1)线程a (消费者)通过条件变量对象 等待一个条件满足,否则就睡眠式等待
2)线程b(生产者)在条件满足时,通过条件变量通知 唤醒线程a
3)线程a(消费者)接到通知,从睡眠中醒来,继续代码的执行
二、创建线程
# coding=utf8
print('main thread start.')
import threading
from time import sleep
def thread1_entry():
print('child thread 1, start')
sleep(15)
print('child thread 1, end')
t1 = threading.Thread(target=thread1_entry) # 实例化,创建线程对象
t1.start() # start()开始执行新的线程
# 主线程继续往下执行
sleep(10)
print('main thread end.')
# 当系统中所有的线程代码都执行完后,整个进程就结束了
"""
threading.Thread只是创建线程对象
start 才是创建
注意:t1 = threading.Thread(target=thread1_entry()),
括号里面的thread1_entry()如果这样加了括号,就是调用了
这个函数,而这个函数没有return,就是返回none,target=none
"""
执行结果:
三、join的作用
# coding=utf8
import threading
from time import sleep, ctime
"""
join方法,表示主线程等待子线程执行结束,再继续往下执行,等待t1线程结束,此时主线程睡眠
"""
def thread1_entry(nsec):
print('child thread 1, start at:', ctime())
sleep(nsec)
print('child thread 1, end at:', ctime())
def thread2_entry(nsec):
print('child thread 2, start at:', ctime())
sleep(nsec)
print('child thread 2, end at:', ctime())
if __name__=='__main__':
print('main thread start.')
# 创建线程对象, 指定了新线程的入口函数
t1 = threading.Thread(target=thread1_entry, args=(1,)) # 线程里面可以带参数,args指函数的参数,后面是个元组,只有一个元素,要加逗号
t2 = threading.Thread(target=thread2_entry, args=(2,))
# 启动新线程
t1.start()
t2.start()
# 等t1 线程结束
t1.join()
# 等t2 线程结束
t2.join()
print('main thread end.')
执行结果:
四、多线程使用局部变量
# coding=utf8
import threading
from time import sleep
def thread_entry():
# 注意 局部变量 var 的值 ,会被两个线程搞乱吗?
var = 1
for i in range(10):
print('th #{} :{}'.format(threading.currentThread().ident, var)) # 获得当前线程对象,ident表示线程的id号
sleep(1)
var += 1
if __name__=='__main__':
print('main thread start.')
t1 = threading.Thread(target=thread_entry)
t2 = threading.Thread(target=thread_entry)
t1.start()
t2.start()
t1.join()
t2.join()
print('main thread end.')
"""
对于函数的局部变量,局部变量是每个线程独有的资源,不会产生冲突的,每个线程都会有局部变量的拷贝;
而全局变量是共享的,是会产生冲突,要控制
"""
执行结果:
五、多线程使用共享数据
# coding=utf8
import threading
from time import sleep
# 存储支付宝账号余额
zhifubao = {
'jcy' : 2000,
'liming' : 5000,
'wangan' : 15000,
'zhaolei' : 6005000,
}
# 线程1 : 滴滴打车处理,参数是用户账户和扣款金额
def thread1_didi_pay(account,amount):
print('* t1: get balance from bank')
balance = zhifubao[account]
# 下面的sleep(2) 表示一些处理过程需要花上2秒钟
print('* t1: do something(like discount lookup) for 2 seconds')
sleep(2)
print('* t1: deduct')
zhifubao[account] = balance - amount
# 线程2 : 余额宝处理,参数是用户账户和当前利息
def thread2_yuebao_interest(account,amount):
print('$ t2: get balance from bank')
balance = zhifubao[account]
# 下面的sleep(1) 表示一些处理过程需要花上1秒钟
print('$ t2: do something2.... for 1 seconds')
sleep(1)
print('$ t2: add')
zhifubao[account] = balance + amount
t1 = threading.Thread(target=thread1_didi_pay, args=('jcy',10))
t2 = threading.Thread(target=thread2_yuebao_interest, args=('jcy',10))
t1.start()
t2.start()
t1.join()
t2.join()
print('finally, jcy balance is %s' % zhifubao['jcy'])
"""
正常来说,金额应该不变的,但是由于使用共享数据,导致的问题,
2个线程同时start,同时使用的是共享的数据2000,第二个线程
先结束,变成2010,存回列表,但是第一个线程此时使用的还是开始的2000,
第一个线程结束后,就是1990,覆盖掉2010;
解决方法,加锁。
"""
执行结果:
六、对共享数据加锁
# coding=utf-8
import threading
from time import sleep
zhifubao ={
'jcy' : 2000,
'liming' : 5000,
'wangan' : 15000,
'zhaolei' : 6005000,
}
# 调用 Lock函数,返回一个锁对象
zhifubao_lock = threading.Lock()
def thread1_didi_pay(account,amount):
# 在代码访问共享对象之前 加锁
# 当多个线程同时执行lock.acquire()时,
# 只有一个线程能成功地获取锁,然后继续执行代码
# 其他线程就继续等待,直到获得锁为止。
zhifubao_lock.acquire()
print('* t1: get balance from bank')
balance = zhifubao[account]
print('* t1: do something(like discount lookup) for 2 seconds')
sleep(2)
print('* t1: deduct')
zhifubao[account] = balance - amount
# 访问完共享对象 释放锁
# 访问结束后,一定要调用Lock对象的release方法,进行解锁操作。
# 否则其它等待锁的线程将永远等待下去,成为死线程。
zhifubao_lock.release()
def thread2_yuebao_interest(account,amount):
# 在代码访问共享对象之前 加锁
zhifubao_lock.acquire()
print('$ t2: get balance from bank')
balance = zhifubao[account]
print('$ t2: do something2... for 1 seconds')
sleep(1)
print('$ t2: add')
zhifubao[account] = balance + amount
# 访问完共享对象 释放锁
zhifubao_lock.release()
t1 = threading.Thread(target=thread1_didi_pay, args=('jcy',10))
t2 = threading.Thread(target=thread2_yuebao_interest, args=('jcy',10))
t1.start()
t2.start()
t1.join()
t2.join()
print('finally, jcy balance is %s' % zhifubao['jcy'])
执行结果:
七、生产者消费者
# coding=utf-8
import threading,time
from random import randint
# 存放共享资源的 列表
commandList =[]
# 创建锁对象
cv = threading.Lock()
# 生产者线程
def thread_producer():
global commandList
cmdNo = 0
while True:
cmdNo += 1
# 这里生产的资源,就先用一个字符串来表示
resource = 'command_{cmdNo}'
# 随机等待一段时间,表示 生产资源的时间,就是输入命令耗费的时间
# 其中参数a是下限,参数b是上限,生成的随机数n:a<=n<=b
time.sleep(randint(3,3))
# 生产好了后,先申请锁
cv.acquire()
#申请锁成功后, 资源 存放入 commandList (共享对象)中
commandList.append(resource)
print('produce resource %s' % resource)
# 释放锁
cv.release()
# 消费者线程,
def thread_consumer ():
global commandList
while True:
# 先申请锁
cv.acquire()
resource = None
# 拿出 生产者线程 产生的一个资源,也就是一个命令
if commandList:
# 表示,已经被本消费者取出该资源了
resource = commandList.pop(0)
# 取出一个共享资源后释放锁(生产者线程就可以对共享资源进行操作了)
cv.release()
if resource != None:
# 随机等待一段时间,表示 消费资源的时间
time.sleep(randint(1, 3))
print('consume resource %s' % resource)
# 注意上面的代码,当commandList里面没有 命令的时候
# 就会不停的执行空循环,非常耗CPU资源
if __name__=='__main__':
t1 = threading.Thread(target=thread_producer)
t2 = threading.Thread(target=thread_consumer)
t1.start()
t2.start()
t1.join()
t2.join()
执行结果:
八、条件变量
# coding=utf-8
import threading,time
from random import randint
commandList =[]
# 调用 Condition,返回一个条件对象, 该对象包含了一个锁对象
cv = threading.Condition()
# 消费者线程
def thread_consumer ():
global commandList
while True:
# 先申请锁,条件变量中包含了锁,可以调用acquire
cv.acquire()
# 如果命令表为空 调用条件变量wait方法 ,该调用会释放锁,并且阻塞在此处,
# 直到生产者 生产出资源后,调用 该条件变量的notify , 唤醒 自己
# 一旦被唤醒, 将重新获取锁(所以生产者线程此时不能对共享资源进行操作)
while commandList == []:
cv.wait()
resource = None
# 拿出 生产者线程 产生的一个资源
if commandList:
# 表示,已经被本消费者取出该资源了
resource = commandList.pop(0)
# 取出一个共享资源后释放锁(生产者线程就可以对共享资源进行操作了)
cv.release()
if resource != None:
# 随机等待一段时间,表示 消费资源的时间
time.sleep(randint(1, 3))
print('consume resource %s' % resource)
# 生产者线程
def thread_producer():
global commandList
cmdNo = 0
while True:
cmdNo += 1
# 这里生产的资源,就先用一个字符串来表示
resource = 'command_{cmdNo}'
# 随机等待一段时间,表示生产资源的时间
time.sleep(randint(3,3))
# 通过条件变量 先申请锁
cv.acquire()
#申请锁成功后, 资源 存放入commandList 中
commandList.append(resource)
print('produce resource %s' % resource)
# 随后调用notify,就像说 有任务啦,等任务的线程来处理吧。。
# 该调用会唤醒一个 阻塞在该条件变量上等待的消费者线程
cv.notify()
# 当然也要释放一下condition里面的锁
cv.release()
if __name__=='__main__':
t1 = threading.Thread(target=thread_producer)
t2 = threading.Thread(target=thread_consumer)
t1.start()
t2.start()
t1.join()
t2.join()
九、进程
from multiprocessing import Process,Pool
import os,time
def proc1_entry(name):
print ('Run child process 1: %s (%s)...' % (name, os.getpid()))
time.sleep(30)
def proc2_entry(name):
print ('Run child process 2: %s (%s)...' % (name, os.getpid()))
time.sleep(30)
if __name__=='__main__':
print ('Parent process %s.' % os.getpid())
p1 = Process(target=proc1_entry, args=('p1',))
p2 = Process(target=proc2_entry, args=('p2',))
p1.start()
p2.start()
p1.join()
p2.join()
print ('Process end.')
执行结果:
十、实例
"""
大概流程:
三个线程,随机选择两个项目,每个项目随机生成三个模板配置
"""
# -*- coding:utf-8 -*-
import requests
import logging
import json
import time
import threading
import random
import PyMySQL
TOKEN = 'SECRET'
HEADERS = {
'X-Auth-Token': TOKEN,
'Content-Type': 'application/json'
}
HOST = '10.246.46.60:9090'
def get(url, project):
headers = HEADERS.copy()
headers['X-Auth-Project'] = project
try:
resp = requests.get(url, headers=headers)
resp.raise_for_status()
return resp.json()
except (requests.RequestException, ValueError):
logging.exception("Url: '%s', Prj: '%s'", url, project)
def post(url, project, payload):
headers = HEADERS.copy()
headers['X-Auth-Project'] = project
try:
resp = requests.post(url, headers=headers, json=payload)
resp.raise_for_status()
return resp.json()
except (requests.RequestException, ValueError):
logging.exception("Url: '%s', Prj: '%s'", url, project)
def get_all_project():
projects=["bobo", "qa", "dh2", "ocean", "cc", "g18", "g17", "whhh17", "opsys", "dev", "g37", "gdc", "cbg", "zhh", "g75", "mhxy", "g78", "uu", "a1", "xy3", "g83", "buzz", "g4", "cld", "g29", "g5", "h42", "a4", "g20", "g58kr", "elk", "g58jp", "pp", "g100", "h45","sapractice", "ngsocial", "g66", "galaxy", "push"]
rand_projec=random.sample(projects,2)
return rand_projec
def is_finished(stat_id, prj):
url = 'http://%s/api/v1/configgens/stat_info/%s' % (HOST, stat_id)
result = get(url, prj)
if result:
return result.get('stat_info', {}).get('finish', False)
def id_get(project):
db = mysql.connect(user="galaxy2", passwd="galaxy2", db="galaxy2", host="10.246.46.60", port=19855, charset='utf8')
db.autocommit(True)
cur = db.cursor()
sql='select aa.id,bb.projectCode from configtpl aa LEFT JOIN project bb on bb.id=aa.pid where projectCode=%s'
cur.execute(sql,(project,))
arr=[]
for i in cur.fetchall():
arr.append(i[0])
db.close()
if len(arr) <=3:
return arr
else:
rand_id=random.sample(arr,3)
return rand_id
def generate_all(prj):
url = 'http://%s/api/v1/configgens/generate' % HOST
id=id_get(prj)
payload = {
"type": "configtpl", "configtpl_ids": id
}
print id,payload
result = post(url, prj, payload)
if result:
return result.get('detail', {}).get('stat_id')
def main():
while True:
time.sleep(100)
projects = get_all_project()
for p in projects:
stat_id = generate_all(p)
print stat_id
if not stat_id:
logging.error("'generate_all(%s)' failed", p)
continue
start_time = time.time()
stat_error = 0
while True:
time.sleep(5)
finished = is_finished(stat_id, p)
if finished:
logging.info("generated: %s", p)
break
if finished is None:
stat_error += 1
if stat_error > 1:
logging.error("get_stat(%s) failed", p)
break
else:
continue
if time.time() - start_time > 400:
logging.error("generate timeout: %s", p)
break
if __name__ == "__main__":
logging.basicConfig(level=logging.INFO, format='%(levelname)s:%(asctime)s:%(message)s')
logging.getLogger('requests').setLevel(logging.ERROR)
t_objs=[]
for i in range (3):
t=threading.Thread(target=main,args=())
t.setDaemon(True)
t.start()
t_objs.append(t)
for t in t_objs: #循环线程实例列表,等待所有线程执行完毕
t.join()