老男孩14期自动化运维day9随笔和作业(多线程批量管理主机)(二)

线程与进程

1.线程:

os调用CPU进行运算的最小单位,被包含在进程中(就是一堆指令)

小知识点
运算速度比较:CPU>RAM>>磁盘
CPU 稍大于RAM(内存),RAM远大于磁盘
每一个程序的内存都是独立的,不能互相访问
单核CPU只能同时执行一个任务,但是因为太快了,在CPU内进行上下文切换(线程的上下文本质上是一组CPU的寄存器,有正在执行程序中的指针及堆栈指针。)
(1)以下是一个线程实例:

# coding:utf-8
# Author:Yang

import threading
import  time

start_time=time.time()
# 多线程
def run(n):
    print("task",n)
    time.sleep(2)
    print("task done",n)


t_objs=[] # 存线程实例

for i in range(50):

    t =threading.Thread(target=run,args=("t%s"%i,))
    t.start()
    t_objs.append(t)  # 为了不阻塞后面线程的启动,不在这里join,先放到一个列表里
for t in t_objs: # 想查看所有线程执行完的时间必须先创建个线程的列表 ,并for循环
    t.join()

print(threading.current_thread(),threading.active_count()) # mainthread、 当前活跃线程
print("cost:",time.time()-start_time)

# 一个进程至少有一个线程,这段程序本身就是一个主线程,最后的cost时间是主线程执行的时间,所以说cost里没有2s,在for循环里只是开辟了子线程的执行
# 默认情况下主线程是不会等子线程执行完毕的

上述代码中,实例化线程写法为

t=treading.Thread(target=方法名,args=(参数,))
注意args中参数是一个元组,最后一个不写就是默认none t.start

一个进程至少有一个线程,这段程序运行时候本身起了一个主线程,最后的cost时间是主线程执行的时间,所以说cost里没有2s,在for循环里只是开辟了子线程的执行
默认情况下主线程是不会等子线程执行完毕的

那么如何去检测所有子线程执行结束的时间?

在python中使用t.join()是等待线程结束,在java,c#中是wait(),相当于不等待结果就不会往下走,变成串行的了

需要检测所有子线程执行结束时间,如下:

for i in range(50):

    t =threading.Thread(target=run,args=("t%s"%i,))
    t.start()
    t_objs.append(t)  # 为了不阻塞后面线程的启动,不在这里join,先放到一个列表里
for t in t_objs: # 想查看所有线程执行完的时间必须先创建个线程的列表 ,并for循环
    t.join()

(2)除了过程写法 还有类式写法(用的较少):

#!/usr/bin/env python
# coding:utf-8
# Author:Yang

import threading
import time


# 类的写法来写多线程
# 这样写比较复杂

class MyThread(threading.Thread):
    def __init__(self,n,sleep_time):
        self.n=n
        self.sleep_time=sleep_time
        super(MyThread,self).__init__() # 因为继承父类 要重构父类的构造方法

    def run(self): # 只能是run
        print("running task",self.n)
        time.sleep(self.sleep_time)
        print("task done..",self.n)
t1=MyThread("t1",2)
t2=MyThread("t2",4)

t1.start()
# t1.join()  # 其实就是有些语言中的wait() 等待线程结束 相当于不等待结果就不会往下走,变成串行的了
t2.start()

t1.join()
t2.join()
print("main thread...")

(3)多线程 守护线程

通过t.setDaemon(True)
把当前线程设置成守护线程(主线程结束都退出,不关心守护线程是否结束,例如写socket时,建立一个连接就是一个线程,当程序断开,所有socket都断开)

for i in range(50):

    t =threading.Thread(target=run,args=("t%s"%i,))
    t.setDaemon(True) # 把当前线程设置成守护线程(主线程结束都退出 不关心守护线程是否结束),就不用加join了
    t.start()
    t_objs.append(t)  # 为了不阻塞后面线程的启动,不在这里join,先放到一个列表里

(4)互斥锁

需求,当一个线程在改全局变量的时候,防止另外的线程来改数据,需要加互斥锁。在python3.x不用加锁了,2.x在ubuntu上要加锁

#!/usr/bin/env python
# coding:utf-8
# Author:Yang
import threading
import  time

# 互斥锁
start_time=time.time()
def run(n):
    lock.acquire() # 获取锁
    global num # 声明全局变量了
    num+=1
    lock.release()
lock=threading. Lock # 释放锁

# python 3.x不用加锁了 2.x在ubuntu上要加锁
num =0
t_objs=[]

for i in range(50):

    t =threading.Thread(target=run,args=("t%s"%i,))
    t.start()
    t_objs.append(t)
for t in t_objs:
    t.join()

print("-----all finished---")
print("num:",num)

在 python2.x的ubuntu上会出现结果为(“num”,49)发现不是50了,说明有线程在改数据的时候,另外的线程也在操作数据

(5)递归锁
递归锁的使用场景:内部锁过多的时候
threading.Lock 内部锁过多会卡死,使用RLock递归锁 可以解除死锁

import threading, time

# 递归锁的使用场景 (内部锁过多的时候)

def run1():
    print("grab the first part data")
    lock.acquire()
    global num
    num += 1
    lock.release()
    return num


def run2():
    print("grab the second part data")
    lock.acquire()
    global num2
    num2 += 1
    lock.release()
    return num2


def run3():
    lock.acquire()
    res = run1()
    print('--------between run1 and run2-----')
    res2 = run2()
    lock.release()
    print(res, res2)




num, num2 = 0, 0
lock = threading.RLock() # threading.Lock 卡死 使用RLock 递归锁 可以解除死锁
for i in range(1):
    t = threading.Thread(target=run3)
    t.start()

while threading.active_count() != 1:
    print(threading.active_count())
else:
    print('----all threads done---')
    print(num, num2)

(6)信号量

semaphore.acquire()
semaphore.release() semaphore =
threading.BoundedSemaphore(5) 最多允许五个线程同时运行,实际上是每出来一个就放进去一个

import threading, time
# 信号量

def run(n):
    semaphore.acquire() # 信号量锁
    time.sleep(1)
    print("run the thread: %s\n" % n)
    semaphore.release()

if __name__ == '__main__':
    semaphore = threading.BoundedSemaphore(5)  # 最多允许5个线程同时运行  实际上是每出来一个就放进去一个
    for i in range(22):
        t = threading.Thread(target=run, args=(i,))
        t.start()
while threading.active_count() != 1:
    pass  # print threading.active_count()
else:
    print('----all threads done---')
    #print(num)

(7) 多线程典型模型 生产者消费者模型
目的 解耦 排队

import threading,time

import queue


# 生产者消费者模型 解耦 排队
q = queue.Queue(maxsize=10)

def Producer(name):
    count = 1
    while True:
        q.put("骨头%s" % count)
        print("生产了骨头",count)
        count +=1
        time.sleep(0.5)



def  Consumer(name):
    #while q.qsize()>0:
    while True:
        print("[%s] 取到[%s] 并且吃了它..." %(name, q.get()))
        time.sleep(1)



p = threading.Thread(target=Producer,args=("Alex",))
c = threading.Thread(target=Consumer,args=("ChengRonghua",))
c1 = threading.Thread(target=Consumer,args=("王森",))


p.start()
c.start()
c1.start()

(8)event 事件 :

事件:线程可以关注另一个线程状态而做出的相应的响应
标志位:event.set() 先绿灯,默认True为绿灯
event.clear() 清空标志位 代表红灯
event.wait() 等待标志位

# 事件 线程可以关注另一个线程状态而做出相应的响应
import time
import threading

event=threading.Event()
def lighter():  # 路灯
    count = 0
    event.set() # 先绿灯 默认true为绿灯
    # event.clear() 清空标志位 代表红灯
    # event.wait() 等待标志位
    while True:
        if count >5 and count<10:
            event.clear() # 把标志位清了
            print("\033[41;1mred light is on...\033[0m")
        elif count >10:
            event.set() # 变绿灯
            count =0
        else:
            print("\033[42;1mgreen light is on...\033[0m")
        time.sleep(1)
        count+=1

def car(name):
    while True:
        if event.is_set(): # 代表绿灯
            print("[%s] running..."%name)
            time.sleep(1)
        else:
            print("[%s] sees red light,waiting..."%name)
            event.wait()
            print("\033[34;1m[%s] green light is on ,start going...\033[0m"%name)

light = threading.Thread(target=lighter,)
light.start()

car1=threading.Thread(target=car,args=("Tesla",))
car1.start()

2.进程:
程序要以一个整体的形式暴露给操作系统管理,里面包含对各种资源的调用,内存的管理,网络接口的调用等。。。所以线程可以定义为:

对一个程序各种资源管理的集合就叫做进程

进程要操作CPU,必须要先创建一个线程(至少一个)进程本身不具有执行功能,而是线程调用CPU
所有在同一个进程的线程共享同一块内存空间

3.进程与线程的区别:
(1)启动线程更快,但是在运行时进程与线程的速度一样
(2)线程共享内存空间,进程的内存是独立的
(3)线程与父进程共享内存数据,父进程数据改变后,线程的数据会改变。子进程是克隆父进程的数据,当父进程数据改变后,不会影响到子进程的数据
(4)同一个进程的线程之间可以直接交流。两个进程想通信,必须通过中间代理来实现
(5)创建新线程很简单。创建新进程需要对其父进程进行一次克隆
(6)一个线程可以控制和操作同一进程里的其他线程,但是进程只能操作其子进程
(7)对一个线程进行需改会影响到同一进程的其他线程。对进程进行修改,但是子进程不会被修改

4.python的设计缺陷(python GIL —global interperter lock 全局解释器锁 )
注意,只是C python解释器的缺陷 像 JPython 等没有这个缺陷,像新的pypy解释器推出了无GIL的版本,用的最多的还是Cpython

缺陷:无论多少核CPU
永远都是在同一时间只有一个线程在运行(因为GIL),所以说python是假并行,因为python起的是通过cpython解释器下c语言调用系统的原生线程

5.队列(Queue)
作用:解耦,使程序直接实现松耦合,提高执行效率
class queue.Queue() # 先进先出
class queue.LifoQueue() # last in first out 后进先出
class queue.PriortyQueue() # 存储数据时可设置优先级队列

#!/usr/bin/env python
# coding:utf-8
# Author:Yang

import queue

# q=queue.Queue(maxsize=2)
# q=queue.LifoQueue()
# q.put("d1")
# q.put("d2")
#
# print(q.qsize())
# print(q.get())
# print(q.get())

q=queue.PriorityQueue()

q.put((-1,"a"))
q.put((10,"yang"))
q.put((3,"b"))
q.put((6,"c"))

print(q.get())
print(q.get())
print(q.get())
print(q.get())

猜你喜欢

转载自blog.csdn.net/qq_33060225/article/details/84319378