python全栈开发,Day42(Thread类的其他方法,同步锁,死锁与递归锁,信号量,事件,条件,定时器,队列,Python标准模块--concurrent.futures)

昨日内容回顾

线程

什么是线程?

线程是cpu调度的最小单位

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

进程和线程是什么关系?

  线程是在进程中的一个执行单位

  多进程 本质上开启的这个进程里就有一个线程

  多线程 单纯的在当前进程中开启了多个线程

线程和进程的区别:

扫描二维码关注公众号,回复: 877056 查看本文章

  线程的开启 销毁 任务切换的时间开销小

  在同一个进程中数据共享

  能实现并发,但不能脱离进程

  进程负责管理分配资源 线程负责执行代码

GIL锁 --  全局解释器锁

同一时刻只能有一个线程访问CPU -- 线程锁

Cpython会受到GIL影响

而pypy和jpython不会受到GIL影响

python程序效率下降的问题

高计算型 -- 多线程会导致程序的效率下降

高IO型的 -- 可以使用多线程,不会受到影响

多进程

分布式计算 -- celery(Python开发的分布式任务调度模块)

启动简单线程

from threading import Thread


def func():
    print(123)


Thread(target=func).start()  

执行输出:123

守护线程和守护进程的区别?

守护线程是等待主进程代码结束之后就结束

守护线程是等待主线程都结束之后才结束

主线程等待其他线程结束,才结束。

开启线程的第二种方法:使用类继承

from threading import Thread
import time


class Sayhi(Thread):
    def __init__(self, name):
        super().__init__()
        self.name = name

    def run(self):
        time.sleep(2)
        print('%s say hello' % self.name)


if __name__ == '__main__':
    t = Sayhi('egon')
    t.start()
    print('主线程')  

执行输出:

一、Thread类的其他方法

Thread实例对象的方法
  # isAlive(): 返回线程是否活动的。
  # getName(): 返回线程名。
  # setName(): 设置线程名。

threading模块提供的一些方法:
  # threading.currentThread(): 返回当前的线程变量。
  # threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
  # threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。

举例:

from threading import Thread


def func():
    print(123)


t = Thread(target=func)
t.start()
print(t.is_alive())  # 返回线程是否是活动的  

执行输出:

睡0.1秒

import time
from threading import Thread


def func():
    time.sleep(0.1)
    print(123)


t = Thread(target=func)
t.start()
print(t.is_alive())  

执行输出:

import time
from threading import Thread


def func():
    time.sleep(0.1)
    print(123)


t = Thread(target=func)
t.start()
print(t.is_alive())  # 返回线程是否活动的
print(t.getName())  # 返回线程名
t.setName('t1')  # s设置线程名
print(t.getName())

import time
from threading import Thread, currentThread, enumerate, activeCount


def func():
    time.sleep(0.1)
    # print(123)


t = Thread(target=func)
t.start()
print(currentThread)  # 返回当前的线程变量
print(enumerate())  # 返回一个包含长在运行的线程的list
print(activeCount())  # 返回正在运行的线程数量  

执行输出:

MainThread, started 9792表示主线程,Thread-1, started 8420表示子线程。它会打印出2个。所以activeCount的结果为2

二、同步锁

当多线程争夺锁时,允许第一个获得锁的线程进入临界区,并执行代码。所有之后到达的线程将被阻塞,直到第一个线程执行结束,退出临界区,并释放锁。  

多个线程抢占资源的情况:

import time
from threading import Thread


def func():
    global n
    temp = n
    time.sleep(1)
    n = temp - 1


n = 100
t_lst = []
for i in range(100):
    t = Thread(target=func)
    t.start()
    t_lst.append(t)
for t in t_lst: t.join()
print(n)  

执行输出:99

为啥呢?

明明减了100次,结果应该是0的。

为啥是99呢?难道是GIL的问题?但GIL是计算CPU哪一刻的锁 

下面开始具体分析:

第一步,每个线程执行global n:temp = n此时,temp等于100

第二步,当线程涉及到CPU计算时,向CPU发送请求。但是受到GIL的限制

同一时刻,只能有一个线程计算。

 

CPU计算结果后,返回给线程。线程赋值,并修改全局变量n,此时n=99,线程结束

那么其他线程,也是做同样的操作

每个线程赋值n等于99,不管它已经是99了。

上面的现象,出现了数据不安全的情况

最后赋值了100次,都是n=99.所以最终结果是99

怎么解决呢?加锁

from threading import Thread, Lock


def func(lock):
    global n
    lock.acquire()  # 加锁
    temp = n
    n = temp - 1
    lock.release()  # 解锁


n = 100
t_lst = []
lock = Lock()  # 创建锁
for i in range(100):
    t = Thread(target=func, args=(lock,))
    t.start()
    t_lst.append(t)
for t in t_lst: t.join()  # 等待所有子线程结束
print(n)  

执行输出:0

如果把计算和赋值拆开,就会出现数据不安全的情况 

下面的写法,不用加锁,也可以得到0

from threading import Thread


def func():
    global n
    n -= 1


n = 100
for i in range(100):
    t = Thread(target=func)
    t.start()
print(n)  

执行输出:0

因为默认有一个GIL锁,所以每个线程都减等1,所以最终结果为0 

三、死锁与递归锁

进程也有死锁与递归锁,在进程那里忘记说了,放到这里一切说了额

所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程,如下就是死锁

猜你喜欢

转载自www.cnblogs.com/Black-rainbow/p/9048482.html