python 多线程

threading 库的使用

函数的线程:

import time
import threading

def coding():
    for x in range(3):
        print("正在写代码",x)
        time.sleep(1)

def drawing():
    for x in range(3):
        print("正在画画",x)

def main():
    设置两个线程,target="这里填函数"
    t1=threading.Thread(target=coding)
    t2=threading.Thread(target=drawing)
	
	让线程开始
    t1.start()
    t2.start()

if __name__ == '__main__':
    main()

运行结果:

正在写代码 0
正在画画 0
正在画画 1
正在画画 2
正在写代码 1
正在写代码 2

2.查看有多少个线程:

print(threading.enumerate())

3.查看现在的线程:

threading.current_thread()

类的线程

1.首先这个类需要继承threading.Thread
2.必须在类的run 方法中实现
3.建立一个对象就可以使用线程了

import time
import threading

class codingthread(threading.Thread): #继承threading.Thread
    def run(self): # 使用run 方法
        for x in range(3):
            print("在写代码",threading.current_thread())
            time.sleep(1)

class Drawingthreading(threading.Thread):
    def run(self):
        for x in range(3):
            print("正在画画",threading.current_thread())
            time.sleep(1)

def main():
	# 建立对象就可以让线程开始
    t1=codingthread()
    t2=Drawingthreading()

    t1.start()
    t2.start()

if __name__ == '__main__':
    main()

运行结果:

在写代码 <codingthread(Thread-1, started 2532)>
正在画画 <Drawingthreading(Thread-2, started 15384)>
正在画画在写代码 <codingthread(Thread-1, started 2532)>
 <Drawingthreading(Thread-2, started 15384)>
在写代码正在画画 <Drawingthreading(Thread-2, started 15384)>
 <codingthread(Thread-1, started 2532)>

多线程共享全局变量的问题:

多线程都是在一个进程中运行的,因此在线程中的全局变量所有的线程都是可以共享的,这就造成了一个问题,因为线程的执行的顺序是无顺序的,可能会造成数据错误!
例子:

import threading

value=0
def add():
    global value
    for x in range(1000):
        value+=1
    print(value)

def main():
    for x in range(2):
        t=threading.Thread(target=add)
        t.start()
if __name__ == '__main__':
    main()

结果:

1000
2000

这个线程没有出错,是因为数字太小了很快就只执行完成了,
再试试下面一个:

import threading

value=0
def add():
    global value
    for x in range(1000000):
        value+=1
    print(value)

def main():
    for x in range(2):
        t=threading.Thread(target=add)
        t.start()
if __name__ == '__main__':
    main()

运行结果:

1518705
1705246

为什么会出错呢? 就是因为多线程的无序问题导致出错了.
又该如何解决呢?
使用锁

使用锁

创建一个锁名为:gLock=threading.Lock()
将需要锁住的地方锁上:
gLock.acquire()
锁完之后需要解锁
gLock.release()

import threading

value=0
# 创建锁
gLock=threading.Lock()
def add():
    global value
    # 将这个地方锁住
    gLock.acquire()
    for x in range(1000000):
        value+=1
    # 将锁解开
    gLock.release()
    print(value)

def main():
    for x in range(2):
        t=threading.Thread(target=add)
        t.start()
if __name__ == '__main__':
    main()

运行结果

1000000
2000000

lock 版生产者和消费者模式(经典例子)

接下来的例子是使用了一个线程生产,一个线程消费,利用的机制改全局变量

在这里插入图片描述

import time
import threading
import random

gMoney=1000 # 一开始的钱
gtimes=10 # 需要赚钱的次数
try_times=0 # 已经赚钱的次数

# 建立一个锁
gLock=threading.Lock()
# 继承了 threading.Thread
class Product(threading.Thread):
    def run(self):
    # 定义全局变量
        global gMoney
        global gtimes
        global try_times
        while True:
        # 要改变全局变量时进行加锁
            gLock.acquire()
            money=random.randint(100,1000)
            # 没有超过gtimes的次数继续赚钱
            if(gtimes>=try_times):
                gMoney+=money
                print("{}生产了{}元钱,还剩{}钱".format(threading.current_thread(),money,gMoney))
                try_times+=1
            else:
            # 超过了gtimes 的次数不继续赚钱
                # 记得要先解锁 ,在break
                gLock.release()
                break
            gLock.release()
            time.sleep(1)
# 继承 threading.Thread
class Consumer(threading.Thread):
    def run(self):
    # 声明全局变量
        global gMoney
        while True:
            gLock.acquire()
            money=random.randint(100,1000)
            # 判断钱还够不够用?
            if(money<=gMoney):
                gMoney-=money
                print("{}消费了{}元钱,还剩{}钱".format(threading.current_thread(),money,gMoney))
            else:
                print("{}想消费{}元钱,但是钱不够用了".format(threading.current_thread(),money))
                # 退出时候要先解锁
                gLock.release()
                break
            gLock.release()
            time.sleep(1)

def main():
    # 使用三个产生者 三个线程来赚钱
    for i in range(3):
        t2=Product(name="第{}个生产者".format(i+1))
        t2.start()
    # 使用两个消费者,两个线程来消费钱财
    for i in range(2):
        t1=Consumer(name="第{}个消费者".format(i+1))
        t1.start()


if __name__ == '__main__':
    main()

运行结果:

<Product(1个生产者, started 9268)>生产了775元钱,还剩1775<Product(2个生产者, started 8032)>生产了976元钱,还剩2751<Product(3个生产者, started 15100)>生产了149元钱,还剩2900<Consumer(1个消费者, started 16948)>消费了847元钱,还剩2053<Consumer(2个消费者, started 16004)>消费了576元钱,还剩1477<Product(1个生产者, started 9268)>生产了683元钱,还剩2160<Product(3个生产者, started 15100)>生产了315元钱,还剩2475<Consumer(2个消费者, started 16004)>消费了196元钱,还剩2279<Consumer(1个消费者, started 16948)>消费了400元钱,还剩1879<Product(2个生产者, started 8032)>生产了612元钱,还剩2491<Product(2个生产者, started 8032)>生产了146元钱,还剩2637<Consumer(2个消费者, started 16004)>消费了813元钱,还剩1824<Consumer(1个消费者, started 16948)>消费了170元钱,还剩1654<Product(3个生产者, started 15100)>生产了584元钱,还剩2238<Product(1个生产者, started 9268)>生产了373元钱,还剩2611<Product(3个生产者, started 15100)>生产了992元钱,还剩3603<Product(1个生产者, started 9268)>生产了783元钱,还剩4386<Consumer(1个消费者, started 16948)>消费了658元钱,还剩3728<Consumer(2个消费者, started 16004)>消费了504元钱,还剩3224<Consumer(2个消费者, started 16004)>消费了605元钱,还剩2619<Consumer(1个消费者, started 16948)>消费了671元钱,还剩1948<Consumer(2个消费者, started 16004)>消费了343元钱,还剩1605<Consumer(1个消费者, started 16948)>消费了679元钱,还剩926<Consumer(1个消费者, started 16948)>消费了903元钱,还剩23<Consumer(2个消费者, started 16004)>想消费311元钱,但是钱不够用了
<Consumer(1个消费者, started 16948)>想消费761元钱,但是钱不够用了

Process finished with exit code 0

进行结果分析: 利用锁的机制将全局变量锁定起来,当自己在执行的时候不收其他线程的影响,从而实现线程的不错乱.

Condition版本的生产者与消费者

lock 版本的生产者与消费者模式可以正常运行,但是存在一个不足,在消费中,总是通过 while True 死循环并且上锁的方式去判断钱够不够,上锁是一个很消费cpu 的行为, 因此这种方式不是最好的,还有一种更好的方式是使用threading.Condition 来实现,threading.Condition 可以在没有数据的时候处于堵塞等待状态,一旦有合适的数据了,还可以使用notify 相关的函数来通知其他处于等待状态的线程.这样就可以不用做一些无用的上锁和解锁的操作,可以提高程序的性能,收先对threading.Condition 相关的函数做个介绍,threading.Condition 类似threading.Lock 可以在修改全局数据的时候进行上锁,上锁后继续执行下面的代码.
1.acquire :上锁
2.release :解锁
3. wait :将当前线程处于等待状态,并且会释放锁,可以被其他线程使用notify 和notify_all 函数唤醒. 被唤醒后会继续等待上锁,上锁后继续执行下面的代码.
4. notify :通知某个正在等待的线程,默认是第一个等待的线程
5. notify_all :通知所有正在等待的线程. notif和notify_all 不会释放锁 .并且需要在release 之前调用.
Condition 版的生产者与消费者模式代码如下:

 import time
import threading
import random

gMoney=1000 # 一开始的钱
gtimes=10 # 需要赚钱的次数
try_times=0 # 已经赚钱的次数

# 建立一个锁
gCondition=threading.Condition()
# 继承了 threading.Thread
class Product(threading.Thread):
    def run(self):
    # 定义全局变量
        global gMoney
        global gtimes
        global try_times
        while True:
        # 要改变全局变量时进行加锁
            gCondition.acquire()
            money=random.randint(100,1000)
            # 没有超过gtimes的次数继续赚钱
            if(gtimes>try_times):
                gMoney+=money
                print("{}生产了{}元钱,还剩{}钱".format(threading.current_thread(),money,gMoney))
                try_times+=1
                # 这里提醒等待(gCondition.wait)的开始排队执行
                gCondition.notify_all()
            else:
            # 超过了gtimes 的次数不继续赚钱
                # 记得要先解锁 ,在break
                gCondition.release()
                break
            gCondition.release()
            time.sleep(1)
# 继承 threading.Thread
class Consumer(threading.Thread):
    def run(self):
    # 声明全局变量
        global gMoney
        while True:
            gCondition.acquire()
            money=random.randint(100,1000)
            # 直到剩余的钱比消费的钱多的时候在出来
            while gMoney<money:
                # 进行一次判断如果是超过赚钱次数了,且钱不够直接退出程序
                if gtimes>=try_times:
                    gCondition.release()
                    return
                # 钱不够进行等待,让gCondition 进行提醒,直到提醒到钱够时在退出
                gCondition.wait()
                print("{}准备消费{}元钱,剩余{}元钱,不足!".format(threading.current_thread(),money,gMoney))
            gMoney-=money
            print("{}消费了{}元钱,还剩{}元钱".format(threading.current_thread(),money,gMoney))
            gCondition.release()
            time.sleep(1)

def main():
    # 使用三个产生者 三个线程来赚钱
    for i in range(3):
        t2=Product(name="第{}个生产者".format(i+1))
        t2.start()
    # 使用两个消费者,两个线程来消费钱财
    for i in range(2):
        t1=Consumer(name="第{}个消费者".format(i+1))
        t1.start()


if __name__ == '__main__':
    main()

运行结果:

<Product(1个生产者, started 2768)>生产了304元钱,还剩1304<Product(2个生产者, started 15412)>生产了158元钱,还剩1462<Product(3个生产者, started 16072)>生产了952元钱,还剩2414<Consumer(1个消费者, started 10584)>消费了466元钱,还剩1948元钱
<Consumer(2个消费者, started 9420)>消费了844元钱,还剩1104元钱
<Product(1个生产者, started 2768)>生产了596元钱,还剩1700<Product(2个生产者, started 15412)>生产了533元钱,还剩2233<Consumer(2个消费者, started 9420)>消费了892元钱,还剩1341元钱
<Consumer(1个消费者, started 10584)>消费了210元钱,还剩1131元钱
<Product(3个生产者, started 16072)>生产了296元钱,还剩1427<Consumer(2个消费者, started 9420)>消费了753元钱,还剩674元钱
<Product(1个生产者, started 2768)>生产了161元钱,还剩835<Product(3个生产者, started 16072)>生产了397元钱,还剩1232<Consumer(1个消费者, started 10584)>消费了723元钱,还剩509元钱
<Product(2个生产者, started 15412)>生产了263元钱,还剩772<Product(2个生产者, started 15412)>生产了692元钱,还剩1464<Consumer(1个消费者, started 10584)>消费了619元钱,还剩845元钱
<Consumer(2个消费者, started 9420)>消费了234元钱,还剩611元钱
<Consumer(2个消费者, started 9420)>消费了189元钱,还剩422元钱
<Consumer(1个消费者, started 10584)>消费了253元钱,还剩169元钱

Process finished with exit code 0

运行结果分析: 利用锁的原理,和等待的和提醒的原理进行对全局变量的灵活应用,减少了锁的使用,提高了代码的效率

Queue线程安全队列:

在线程中,访问一些全局变量,加锁是一个经常的过程。如果你是想把一些数据存储到某个队列中,那么Python内置了一个线程安全的模块叫做、queue模块。Python中的queue模块中提供了同步的、线程安全的队列类,包括FIFO(先进先出)队列Queue,LIFO(后入先出)队列LifoQueue。这些队列都实现了锁原语〈可以理解为原子操作,即要么不做,要么都做完),能够在多线程中直接使用。可以使用队列来实现线程间的同步。相关的函数如下:
1.初始化Queue(maxsize):创建一个先进先出的队列。
2.qsize):返回队列的大小。
3.empty(:判断队列是否为空。
4. full():判断队列是否满了。
5.get():从队列中取最后一个数据。
5. put(:将一个数据放到队列中。

基本例子:

from queue import Queue

# 定义4个大小的队列
q=Queue(4)
for i in range(4):
    # 放四个值放在队列中
    q.put(i)
# 查看队列的里面有多少个值
print(q.qsize())
# 判断是否为满 ,如果为满则返回True,不为满返回False
print(q.full())
# 判断是否为空队列,如果为空返回True,不为空返回False
print(q.empty())
for i in range(4):
    # 取队列里的值
    q.get()

注意 put 和get 里面有一个参数 block 默认是True 意思 就是会等待值,说的通俗一点 你用get的时候,你需要值得时候没有的时候,他会等待(程序暂停)你给他值,当put没有值传入的时候也会等待(程序暂停)值得传入.

from queue import Queue
import time
import threading

def set_value(q):
    # 定义一个入队函数
    index=0
    while True:
        q.put(index)
        index+=1
        #入队一个睡三秒
        time.sleep(3)

def get_value(q):
    # 定义一个出队函数
    while True:
        print(q.get())

def main():
    # 定义4个大小的队列
    q = Queue(4)
    # 这里函数的对面函数名用target ,函数参数用列表传入到 args 里面
    t1=threading.Thread(target=set_value,args=[q])
    t2=threading.Thread(target=get_value,args=[q])

    t1.start()
    t2.start()
if __name__ == '__main__':
    main()
0
1
2
3
4
5
6
7
8
每大约三秒就会出现一个

结果的分析:
因为 put 和 get 函数里面的参数 block 默认为True,所以它会等待值得来到,所以不会出错.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值