day34:线程&守护线程&线程锁&事件

目录

1.线程的基本使用

2.用类定义线程

3.线程相关的函数

4.守护线程

5.线程中安全问题:Lock

6.信号量:Semaphore

7.死锁 互斥锁 递归锁

8.事件:Event

线程的基本使用

首先,明确一下线程和进程的定义

进程:资源分配的最小单位

线程:cpu执行程序的最小单位

1.一个进程资源中可以包含多个线程

def func(num):
    time.sleep(random.uniform(0.1,1))
    print("当前进程{},参数是{}".format(os.getpid(),num))


for i in range(10): # 在一个进程资源中创建了十个线程
    t = Thread(target=func,args=(i,))
    t.start()

print(os.getpid())

运行结果如下图所示

2.并发的多线程和多进程谁的速度快? 多线程!

计算多线程执行时间

def func(num):
    print("当前进程{},参数是{}".format(os.getpid(), num))


if __name__ == "__main__":
    # 多线程
    lst = []
    # 记录开始时间
    startime = time.time()
    for i in range(1000):
        t = Thread(target=func,args=(i,))
        t.start()
        lst.append(t)

    # 等到所有的子线程执行完毕
    for i in lst:
        i.join()

    # 计算结束时间
    endtime = time.time()
    print("多线程执行时间",(endtime - startime)) # 0.27

如图所示,跑1000个线程只需要0.27s

计算多进程执行时间

def func(num):
    print("当前进程{},参数是{}".format(os.getpid(), num))


if __name__ == "__main__":
    lst = []
    startime = time.time()
    for i in range(1000):
        t = Process(target=func, args=(i,))
        t.start()
        lst.append(t)

    # 等到所有的子线程执行完毕
    for i in lst:
        i.join()

    # 计算结束时间
    endtime = time.time()
    print("多进程执行时间", (endtime - startime))  # 63.30

如图所示,跑1000个进程需要63.30s

所以,我们得出结论!并发的多线程要远比多进程快!!!

3.多线程之间,共享同一份进程资源

num = 1000
def func():
    global num
    num -= 1
    
for i in range(1000):
    t = Thread(target=func)
    t.start()
    
print(num)
    

运行结果如下图所示

用类定义线程

class MyThread(Thread):
    def __init__(self,name):
        # 手动调用父类的构造方法
        super().__init__()
        self.name = name
        
    def run(self):
        time.sleep(1)
        print("当前进程号码是{},名字是{}".format(os.getpid() , self.name))


if __name__ == "__main__":
    t = MyThread("当前是一个线程")
    t.start()
    print("主线程执行结束 ... ")

线程相关的函数

1.线程.is_alive() 检测线程是否仍然存在

2.线程.setName() 设置线程名字

3.线程.getName() 获取线程名字

def func():
    time.sleep(1)

if __name__ == "__main__":
    t = Thread(target=func)
    t.start()
    # 检测线程是否仍然存在
    print(t.is_alive()) # True
    # 获取线程名字
    print(t.getName())  # Thread-1
    # 设置线程名字
    t.setName("xboyww")
    # 获取线程名字
    print(t.getName()) # xboyww

1.currentThread().ident 查看线程id号

def func():
    print("子线程的线程id{}".format(currentThread().ident))

if __name__ == "__main__":
    Thread(target=func).start()
    print("主线程的线程id{}".format(currentThread().ident))

运行结果如下图所示

2.enumerate() 返回目前正在运行的线程列表

3.activeCount() 返回目前正在运行的线程数量

def func():
    print("子线程的线程id{}".format(currentThread().ident))
    time.sleep(0.5)


if __name__ == "__main__":
    for i in range(10):
        Thread(target=func).start()
    lst = enumerate()
    # 主线程 + 10个子线程
    print(lst, len(lst))

    # 3.activeCount() 返回目前正在运行的线程数量
    print(activeCount())  # 11

运行结果如下图所示

守护线程

守护线程和守护进程不同

守护进程是守护主进程,主进程结束,守护进程立刻被杀死

守护线程是守护所有线程,必须等所有线程结束,守护线程才会被杀死

def func1():
    while True:
        time.sleep(0.5)
        print("我是func1")

def func2():
    print("我是func2 start ... ")
    time.sleep(3)
    print("我是func2 end ... ")

def func3():
    print("我是func3 start ... ")
    time.sleep(5)
    print("我是func3 end ... ")

if __name__ == "__main__":
    t1 = Thread(target=func1)
    t2 = Thread(target=func2)
    t3 = Thread(target=func3)
    
    # 在start调用之前,设置线程为守护线程
    t1.setDaemon(True)
    
    t1.start()
    t2.start()
    t3.start()
    
    print("主线程执行结束 .... ")

运行结果如下图所示

线程中安全问题:Lock

现在,我们想完成如下的操作:

  1.在线程p1中for循环100万次,每次完成一次+1操作

  2.在线程p2中for循环100万洗,每次完成一次-1操作

根据想法,我们可以写出如下代码:

n = 0


def func1(lock):
    global n
    for i in range(1000000):
        n += 1


# lock.release()

def func2(lock):
    global n
    for i in range(1000000):
        n -= 1


if __name__ == "__main__":
    lst = []
    lock = Lock()

    startime = time.time()
    for i in range(10):
        t1 = Thread(target=func1, args=(lock,))
        t2 = Thread(target=func2, args=(lock,))
        t1.start()
        t2.start()
        lst.append(t1)
        lst.append(t2)

    for i in lst:
        i.join()

    endtime = time.time()
    print("主线程执行结束 ...  打印{} 时间是{}".format(n, endtime - startime))

执行结果如下图所示

然而,这并不是我们想要的结果

所以我们需要在线程t1和线程t2加锁

运行代码,发现得到的结果正是我们想要的结果

信号量:Semaphore

线程的Semaphore和进程的Semaphore完全一致,在此就不过多赘述了

def func(i,sm):
    # 上锁 + 解锁
    with sm:
        print(i)
        time.sleep(3)    
if __name__ == "__main__":
    # 支持同一时间,5个线程上锁
    sm = Semaphore(5)
    for i in range(20):
        Thread(target=func,args=(i,sm)).start()
    
"""
再创建线程的时候是异步创建
在执行任务时,遇到Semaphore进行上锁,会变成同步程序
"""

死锁 互斥锁 递归锁

1.语法上的死锁

只上锁不解锁,一定会产生死锁

lock = Lock()
lock.acquire()
lock.acquire()
lock.acquire() # 只上锁不解锁,会产生死锁。运行程序会发生阻塞

lock.release()
print(1)

2.逻辑上的死锁

noodle_lock = Lock()
kuaizi_lock = Lock()


def eat1(name):
    noodle_lock.acquire()
    print("%s 抢到面条了" % (name))
    kuaizi_lock.acquire()
    print("%s 抢到筷子了" % (name))

    print("开始享受面条 ... ")
    time.sleep(0.5)

    kuaizi_lock.release()
    print("%s 放下筷子" % (name))
    noodle_lock.release()
    print("%s 放下面条" % (name))


def eat2(name):
    kuaizi_lock.acquire()
    print("%s 抢到筷子了" % (name))
    noodle_lock.acquire()
    print("%s 抢到面条了" % (name))

    print("开始享受面条 ... ")
    time.sleep(0.5)

    noodle_lock.release()
    print("%s 放下面条" % (name))
    kuaizi_lock.release()
    print("%s 放下筷子" % (name))


if __name__ == "__main__":
    name_lst1 = ["Fly", "Hurt"]
    name_lst2 = ["Alan", "Cat"]

    for name in name_lst1:
        Thread(target=eat1, args=(name,)).start()

    for name in name_lst2:
        Thread(target=eat2, args=(name,)).start()

运行结果如下图所示

3.递归锁

上方的示例造成了逻辑上的死锁现象

想要解决这种情况,我们需要递归锁。

什么是递归锁?

  递归锁专门用来解决这种死锁现象

  临时用于快速解决线上项目发生阻塞死锁问题的

from threading import RLock

rlock = RLock()
rlock.acquire()
rlock.acquire()
rlock.acquire()
rlock.acquire()
print(112233)
rlock.release()
rlock.release()
rlock.release()
rlock.release()

print("程序结束 ... ")

运行结果如下图所示

4.用递归锁解决2中(面条-筷子)的死锁现象

noodle_lock = kuaizi_lock = RLock()

def eat1(name):
    noodle_lock.acquire()
    print("%s 抢到面条了" % (name))
    kuaizi_lock.acquire()
    print("%s 抢到筷子了" % (name))

    print("开始享受面条 ... ")
    time.sleep(0.5)

    kuaizi_lock.release()
    print("%s 放下筷子" % (name))
    noodle_lock.release()
    print("%s 放下面条" % (name))

def eat2(name):
    kuaizi_lock.acquire()
    print("%s 抢到筷子了" % (name))
    noodle_lock.acquire()
    print("%s 抢到面条了" % (name))

    print("开始享受面条 ... ")
    time.sleep(0.5)

    noodle_lock.release()
    print("%s 放下面条" % (name))
    kuaizi_lock.release()
    print("%s 放下筷子" % (name))

if __name__ == "__main__":
    name_lst1 = ["Fly","Hurt"]
    name_lst2 = ["Cat","Alan"]

    for name in name_lst1:
        Thread(target=eat1,args=(name,)).start()

    for name in name_lst2:
        Thread(target=eat2,args=(name,)).start()

运行结果如下图所示

5.用互斥锁解决2中(面条-筷子)的死锁现象

用互斥锁解决问题,换句话来说,就是尽量用一把锁解决问题

mylock = Lock()
def eat1(name):
    mylock.acquire()
    print("%s 抢到面条了" % (name))
    print("%s 抢到筷子了" % (name))
    
    print("开始享受面条 ... ")
    time.sleep(0.5)    

    print("%s 放下筷子" % (name))
    print("%s 放下面条" % (name))
    mylock.release()

def eat2(name):
    mylock.acquire()
    print("%s 抢到筷子了" % (name))
    print("%s 抢到面条了" % (name))
    
    print("开始享受面条 ... ")
    time.sleep(0.5)    

    print("%s 放下面条" % (name))    
    print("%s 放下筷子" % (name))
    mylock.release()

if __name__ == "__main__":
    
    name_lst1 = ["A","B"]
    name_lst2 = ["C","D"]
    
    for name in name_lst1:
        Thread(target=eat1,args=(name,)).start()
        
    for name in name_lst2:
        Thread(target=eat2,args=(name,)).start()

事件:Event

e = Event()

wait 动态添加阻塞

clear 将内部的阻塞值改成False

set 将内部的阻塞值改成True

is_set 获取内部的阻塞值状态(True False)

基本语法

e = Event()
print(e.is_set())
e.set()
print(e.is_set())
e.clear()
print(e.is_set())
# 代表最多阻塞3秒
e.wait(3)
print("程序运行中... ")

模拟链接远程数据库

def check(e):
    # 用一些延迟来模拟检测的过程
    time.sleep(random.randrange(1,6)) # 1 2 3 4 5
    # time.sleep(1)
    print("开始检测链接用户的合法性")
    e.set()
    
    
def connect(e):
    sign = False
    for i in range(1,4): # 1 2 3    
        # 设置最大等待1秒
        e.wait(1)    
    
        if e.is_set():
            print("数据库链接成功 ... ")
            sign = True
            break
        else:
            print("尝试链接数据库第%s次失败 ... " % (i))
            
    if sign == False:
        # 主动抛出异常,(超时异常)
        raise TimeoutError
        
e = Event()
# 线程1号负责执行连接任务
Thread(target=connect,args=(e,)).start()

# 线程2号负责执行检测任务
Thread(target=check,args=(e,)).start()

运行结果如下图所示

原文地址:https://www.cnblogs.com/libolun/p/13530797.html