python多进程/线程开发

一、概念

进程的概念:运行着的程序

线程的概念:
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()

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

飘凛枫叶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值