Python---线程的创建、多线程、线程冲突、锁、信号量

线程的创建:
1.线程的创建【使用此方式创建的线程为守护线程】

import _thread
import threading

def fn1(a, b):
    print(a, b)
    print(threading.current_thread().name)

def create_thread1():
    # 参数一:在子线程中要执行的函数
    # 参数二:子线程中的函数需要的参数,注意一定要以元组的形式传参
    # 创建并执行守护线程,守护线程的特点是,当主线程结束,守护线程无论执行完毕都会结束
    _thread.start_new_thread(fn1, ("hello", "yes"))

if __name__ == "__main__":
    create_thread1()
    print(threading.current_thread().name)
>>>MainThread
#注意此时创建的线程中的代码不会执行,原因是
#主线程执行的速度非常快,主线程执行结束,就直接退出了,因此我们的子线程根本不会被创建
#我们可以让主线程阻塞一段时间可以查看一下效果
#time.sleep(3)  #这时候我们就可以查看打印的数据了

2、线程创建的方式二(比较常用)
import threading

def func1(*args):
    print(args)
    print(threading.current_thread().name)

def create_thread2():
    # target:在子线程中要执行的函数
    # deamo:是否为守护线程
    # name:线程名称
    t = threading.Thread(target=func1, daemon=False, name="BIG", args=("hello"))
    t.start()  # 启动线程

if __name__ == "__main__":
    create_thread2()
3、创建线程方式三
import threading
class MyThread(threading.Thread):
    def __init__(self):
        super().__init__()     
    #一定要重写此方法,此方法会自动被调用
    #里面的子线程
    def run(self):
        print("mythread",threading.current_thread().name)

def create_thread3():
    t = MyThread()
    t.start()
    
if __name__ == "__main__":
    create_thread3()

参数及常用函数:
Thread(group=None, target=None, name=None, args=(), kwargs={}) 
  group: 线程组,目前还没有实现,库引用中提示必须是None; 
  target: 要执行的方法; 
  name: 线程名; 
  args/kwargs: 要传入方法的参数。
实例方法: 
  isAlive(): 返回线程是否在运行。正在运行指启动后、终止前。 
  get/setName(name): 获取/设置线程名。 
  start():  线程准备就绪,等待CPU调度
  is/setDaemon(bool): 获取/设置是后台线程(默认前台线程(False))。(在start之前设置)
#如果是后台线程/守护线程,主线程执行过程中,后台线程也在进行,主线程执行完毕后,后台线程不论成功与否,主线程和后台线程均停止
#如果是前台线程,主线程执行过程中,前台线程也在进行,主线程执行完毕后,等待前台线程也执行完成后,程序停止 
  join([timeout]): 阻塞当前上下文环境的线程,直到调用此方法的线程终止或到达指定的timeout(可选参数)

多线程:
import threading,time,random

def fun(*args):
    time.sleep(random.randint(1,4))
    print("子线程",args)
 
if __name__ == "__main__":
    #多个线程同时运行,是异步执行
    t1 = threading.Thread(target=fn,args=("包饺子",))
    t1.start()
    # t1.join()  会阻塞 等待t1线程执行完成后再继续往后执行
     t2 = threading.Thread(target=fn,args=("甜馨",))
    t2.start()
    t3 = threading.Thread(target=fn,args=("嗯哼",))
    t3.start()
    #若多个线程想让其按顺序执行,则可以使用join() 的方法

循环创建线程:
import threading,time,random

def fun(*args):
    time.sleep(random.randint(1,4))
    print("子线程",args)
 

if __name__ == "__main__":
    start = time.time()
    t_list= []
    for x in range(10):
        t1 = threading.Thread(target=fn,args=(x,))
        t1.start()
        t_list.append(t1)
        #当前线程的名称
        print(threading.current_thread().name)
        #线程id
        print(t.ident)
        #线程是否正在执行
        print(t.is_alive())
    #正在运行线程的数量,包括主线程
    print(threading.active_count())
    #列举当前正在运行的所有的线程
    print(threading.enumerate())
    for t in t_list:
        t.join()
    
    print(time.time()-start)

线程冲突:
多个线程并发访问同一个变量而相互干扰
解决方式:线程锁

import threading

def fn():
    global num
    #cpu分配的时间片不足以完成一百万次加法运算
    #因此结果还没有被保存到内存中就可能被其他的线程打断
    for x in range(1000000):
        num += 1

def thread1():
    for i in range(5):
        t = threading.Thread(target=fn)
        t.start()

解决多线程的冲突问题方式一:

lock = threading.Lock()

使用 with lock

with内部实现了enter()和exit()执行语句之前调用enter方法,退出的时候调用exit

import threading

#解决线程冲突问题
#线程锁
lock = threading.Lock()

def fn():
    with lock: #会自动加锁,执行完毕之后才会释放锁
        global num
        #cpu分配的时间片不足以完成一百万次加法运算
        #因此结果还没有被保存到内存中就可能被其他的线程打断
        for x in range(1000000):
            num += 1
        print(num)

def thread1():
    for i in range(5):
        t = threading.Thread(target=fn)
        t.start()

if __name__ == '__main__':
    num = 0
    thread1()

解决多线程冲突问题,方式二

lock = threading.Lock()

lock.acquire() #锁定

lock.release() #释放锁
import threading

#解决线程冲突问题
#线程锁,创建锁
lock = threading.Lock()

def fn():
    lock.acquire() #锁定
    global num
    #cpu分配的时间片不足以完成一百万次加法运算
    #因此结果还没有被保存到内存中就可能被其他的线程打断
    for x in range(1000000):
        num += 1
    print(num)
    lock.release() #释放锁

def thread1():
    for i in range(5):
        t = threading.Thread(target=fn)
        t.start()

if __name__ == '__main__':
    num = 0
    thread1()

死锁:
定义:是指一个资源多次调用,而多次调用对方都未能释放该资源就会造成一种互相等待的现象,若无外力作用,它们都将无法推进下去,此时称系统处于死锁状态或者系统产生了死锁。

若存在两个线程:线程A 与线程B

若线程A与线程B都 需要资源1与资源2才能执行

现在线程A拿到了资源1,线程B拿到了资源2,此时就构成了死锁。

若要解决死锁的问题,则此时我们需要使用递归锁
下面以一个关于线程死锁的经典问题:“哲学家就餐问题”,作为本节最后一个例子。题目是这样的:五位哲学家围坐在一张桌子前,每个人 面前有一碗饭和一只筷子。在这里每个哲学家可以看做是一个独立的线程,而每只筷子可以看做是一个锁。每个哲学家可以处在静坐、 思考、吃饭三种状态中的一个。需要注意的是,每个哲学家吃饭是需要两只筷子的,这样问题就来了:如果每个哲学家都拿起自己左边的筷子, 那么他们五个都只能拿着一只筷子坐在那儿,直到饿死。此时他们就进入了死锁状态。 下面是一个简单的使用死锁避免机制解决“哲学家就餐问题”的实现:
import threading,time

class ZheXueJia():
    def __init__(self,left,right):
        self.left = left
        self.right = right

def run(z,name):
    #获取左筷子
    f = z.left.acquire()
    if f:
        print(name,"获取左筷子...")
    #获取右筷子
    ff = z.right.acquire()
    if ff:
        print(name,"获取右筷子...")
        print("哲学家开始就餐",name)
        time.sleep(1)
        print("就餐完毕",name)
    #释放右筷子
    z.right.release()
    #释放左筷子
    z.left.release()


if __name__ == '__main__':
    #每个筷子相当于一把锁
    rlock1 = threading.RLock()
    rlock2 = threading.RLock()
    rlock3 = threading.RLock()
    rlock4 = threading.RLock()
    rlock5 = threading.RLock()
    #必须同时拥有两把锁才能就餐
    z1 = ZheXueJia(rlock5, rlock1)
    z2 = ZheXueJia(rlock1, rlock2)
    z3 = ZheXueJia(rlock2, rlock3)
    z4 = ZheXueJia(rlock3, rlock4)
    z5 = ZheXueJia(rlock4, rlock5)
    #创建五个线程模仿哲学家
    t1 = threading.Thread(target=run,args=(z1,"z1"))
    t2 = threading.Thread(target=run,args=(z2,"z2"))
    t3 = threading.Thread(target=run,args=(z3,"z3"))
    t4 = threading.Thread(target=run,args=(z4,"z4"))
    t5 = threading.Thread(target=run,args=(z5,"z5"))
    #开启线程
    t1.start()
    t2.start()
    t3.start()
    t4.start()
    t5.start()

银行家算法:
import threading
import time

A = threading.Lock()
B = threading.Lock()

class MyThread(threading.Thread):

    def fun1(self):
        A.acquire()
        print(self.name,"gotA",time.ctime())
        time.sleep(2)
        B.acquire()
        print(self.name,"gotB",time.ctime())
        time.sleep(1)
        B.release()
        A.release()


    def func2(self):
        B.acquire()
        print(self.name,"gotB",time.ctime())
        time.sleep(2)
        A.acquire()
        print(self.name,"gotA",time.ctime())
        time.sleep(1)
        A.release()
        B.release()

    def run(self):
        self.fun1()
        self.func2()

if __name__ == '__main__':
    L = []
    for i in range(5):
        t = MyThread()
        t.start()
        L.append(t)
    for i in L:
        i.join()
    print("over")
运行结果:
Thread-1 gotA Wed May 15 20:47:24 2019
Thread-1 gotB Wed May 15 20:47:26 2019
Thread-1 gotB Wed May 15 20:47:27 2019
Thread-2 gotA Wed May 15 20:47:27 2019
#结果会卡在这儿,原因就是第一个线程执行完fun1后开始执行fun2,而第二个开始执行fun1,它们会对锁产生抢占,所以导致运行卡住形成死锁现象。
或者使用递归锁来解决
# 解决方案:将所有锁改编成递归锁RLock
# 在Python中为了支持在同一线程中多次请求同一资源,python提供了可重入锁RLock。
# 这个RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。
# 直到一个线程所有的acquire都被release,其他的线程才能获得资源。上面的例子如果使用RLock代替Lock,则不会发生死锁。
import threading
import time

# mutexA = threading.Lock() #资源A
# mutexB = threading.Lock()#资源B
RLock = threading.RLock()


class MyThread(threading.Thread):

    def __init__(self):
        threading.Thread.__init__(self)

    # 当对象调用start的时候会自动执行此方法
    def run(self):
        self.fun1()
        self.fun2()

    def fun1(self):
        RLock.acquire()  # 如果锁被占用,则阻塞在这里,等待锁的释放
        # 获取资源A
        print("I am %s , get res: %s" % (self.name, "ResA"))

        RLock.acquire()
        print("I am %s , get res: %s" % (self.name, "ResB"))
        RLock.release()

        RLock.release()

    def fun2(self):
        RLock.acquire()
        print("I am %s , get res: %s" % (self.name, "ResB"))
        time.sleep(0.2)

        RLock.acquire()
        print("I am %s , get res: %s" % (self.name, "ResA"))
        RLock.release()
        RLock.release()

if __name__ == "__main__":
    for i in range(0, 10):
        my_thread = MyThread()
        my_thread.start()

信号量:
semaphore是一个内置的计数器,限制同一时间执行的线程的个数

#每当调用acquire()时,内置计数器-1
#每当调用release()时,内置计数器+1
计数器不能小于0,当计数器为0时,acquire()将阻塞线程直到其他线程调用release()

import time
import threading

def foo():
    time.sleep(2)   #程序休息2秒
    print("ok",time.ctime())

for i in range(200):
    t1=threading.Thread(target=foo,args=()) #实例化一个线程
    t1.start()  #启动线程

程序会在很短的时间内生成200个线程来打印一句话。

如果在主机执行IO密集型任务的时候再执行这种类型的程序时,计算机就有很大可能会宕机。这时候就可以为这段程序添加一个计数器功能,来限制一个时间点内的线程数量。

宕机,指操作系统无法从一个严重系统错误中恢复过来,或系统硬件层面出问题,以致系统长时间无响应,而不得不重新启动计算机的现象。它属于电脑运作的一种正常现象,任何电脑都会出现这种情况。

import time
import threading

s1=threading.Semaphore(5)   #添加一个计数器

def foo():
    s1.acquire()    #计数器获得锁
    time.sleep(2)   #程序休眠2秒
    print("ok",time.ctime())
    s1.release()    #计数器释放锁


for i in range(20):
    t1=threading.Thread(target=foo,args=()) #创建线程
    t1.start()  #启动线程   

或者使用此方法
import time
import threading

sem=threading.Semaphore(5)   #添加一个计数器

def foo():
    with sem:
        time.sleep(2)   #程序休眠2秒
        print("ok",time.ctime())

for i in range(20):
    t1=threading.Thread(target=foo,args=()) #创建线程
    t1.start()  #启动线程

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值