多线程

Posted kend

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程相关的知识,希望对你有一定的参考价值。

# 图片  下载耗时 用多线程
# threading模块
import threading
import time

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

def drawing():
    for i in range(3):
        print("正在画画%s"%i)
        time.sleep(1)

def main():
    # 创建一个子线程
    t1 = threading.Thread(target=coding, )
    t1.start()
    t2 = threading.Thread(target=drawing, )
    t2.start()

    
if __name__ == __main__:
    main()

#####################################
# 通过threading.current_thread() 获取当前的线程对象
def coding():
    for i in range(3):
        print("正在写代码%s" % threading.current_thread())  
        time.sleep(1)

def drawing():
    for i in range(3):
        print("正在画画%s" % threading.current_thread())
        time.sleep(1)

 

# 使用Thread类创建多线程  
# 区别就是继承一下threading.Thread, 把之前的函数的代码写到 def run下面

import threading
import time

class CodingThread(threading.Thread):
    def run(self):
        for i in range(3):
            print("正在写代码%s"%threading.current_thread())
            time.sleep(1)

class DrawingThread(threading.Thread):
    def run(self):
        for i in range(3):
            print("正在画画%s"%threading.current_thread())
            time.sleep(1)


def main():
    # 创建一个子线程
    t1 = CodingThread()
    t1.start()
    t2 = DrawingThread()
    t2.start()


if __name__ == __main__:
    main()

 

# 多线程共享全局变量 和 锁的问题(修改全局变量的时候要加锁,访问全局变量的不用加锁)
# 线程执行的顺序是无序的

import threading

VALUE = 0
gLock = threading.Lock() # 创建锁

def add_value():
    global VALUE
    gLock.acquire() # 加锁
    for x in range(1000000):
        VALUE += 1
    gLock.release() # 解锁
    print("value,%d"%VALUE)

def main():
    for x in range(2):
        t = threading.Thread(target=add_value)
        t.start()

if __name__ == __main__:
    main()
    
# 加锁后执行结果,就不会有错误了
# value,1000000
# value,2000000
# 不加锁的话两个线程会同时修改VALUE的数据, 相当于多线程同时对一个url 进行获取

 

# Lock版的 生产者或消费者模式, 一般都是在多线程中才使用
# 爬虫中生产者专门爬取url, 消费者专门解析url

import threading
import random
import time

gMoney = 1000
gLock = threading.Lock()

class Producer(threading.Thread):
    def run(self):
        global gMoney
        while True:
            money = random.randint(100,1000)
            gLock.acquire()
            gMoney += money
            print("%s生产了%d的钱,现在总共有%d"%(threading.current_thread(),money,gMoney))
            gLock.release()
            time.sleep(1)


class Consumer(threading.Thread):
    def run(self):
        global gMoney
        while True:
            money = random.randint(100,1000)
            gLock.acquire()
            if gMoney >= money:
                gMoney -= money
                print("消费者%s,消费了%d,还剩有%d"%(threading.current_thread(),money,gMoney))
            else:
                print("余额不足,当前金额是%d, 需要消费的金额是%d"%(gMoney,money))
            gLock.release()
            time.sleep(1)

def main():
    for x in range(2):
        t = Producer(name="生产者%d"%x)
        t.start()

    for x in range(3):
        t = Consumer(name=消费者%d%x)
        t.start()


if __name__ == __main__:
    main()
   
# 以上是一个死循环模式 现在要求生产者只生产10次 就停止,
# 消费者把已经生产出来的给消费掉, 消费完毕就停止消费
# 相当于爬虫爬取一定个数的 url 就停止
# 对上面代码修改

import threading
import random
import time

gMoney = 1000
gLock = threading.Lock()
gTimes = 0
gTotalTimes = 10

class Producer(threading.Thread):
    def run(self):
        global gMoney
        global gTimes
        global gTotalTimes
        while True:
            money = random.randint(100,1000)
            gLock.acquire()

            # if gTimes >= gTotalTimes:
            #     gLock.release()
            #     break
            # gMoney += money
            # gTimes += 1
            # print("%s生产了%d的钱,现在总共有%d" % (threading.current_thread(), money, gMoney))
            # gLock.release()
            # time.sleep(1)

            if gTimes < gTotalTimes:
                gMoney += money
                gTimes += 1
                print("%s生产了%d的钱,现在总共有%d"%(threading.current_thread(),money,gMoney))
                gLock.release()
                time.sleep(1)
            else:
                print("已经生产了10次, 停止生产")
                gLock.release()
                break


class Consumer(threading.Thread):
    def run(self):
        global gMoney
        while True:
            money = random.randint(100,1000)
            gLock.acquire()
            if gMoney >= money:
                gMoney -= money
                print("消费者%s,消费了%d,还剩有%d"%(threading.current_thread(),money,gMoney))
            else:
                if gTimes >= gTotalTimes:
                    gLock.release()
                    break
                print("余额不足,当前金额是%d, 需要消费的金额是%d"%(gMoney,money))
            gLock.release()
            time.sleep(1)

def main():
    for x in range(2):
        t = Producer(name="生产者%d"%x)
        t.start()

    for x in range(3):
        t = Consumer(name=消费者%d%x)
        t.start()


if __name__ == __main__:
    main()

# 面试题中有问 怎么确保多线程爬取的数据不会错乱? 可以使用这个 生产者消费者模式
# 给多线程加锁之后, 保证同一个url 不会被爬取两次

 

# condition版的生产者与消费者模式  比lock版本优化了
# lock在while循环中 不断的加锁解锁, 消耗cup,所以不是最好的
# threading.Condition 继承threading.Lock, 它可以在修改全局数据的时候加锁,在修改完毕后解锁

# threading.Condition相关5个函数介绍:
# acquire 上锁, 
# release 解锁
# wait 将当前线程处于等待状态(阻塞), 并且会释放锁,其他线程就可以使用锁了.
# wait 函数可以被notify和notify_all函数唤醒, wait被唤醒后等待上锁, 上锁后继续执行下面代码
# notify 通知某个等待的线程,默认是第一个等待的线程.告诉等待的线程可以去获取锁了
# notify_all 通知所有正在等待的线程. notify和notify_all不会释放锁,需要在release之前使用

# threading.Condition 继承threading.Lock
import threading
import random
import time

gMoney = 1000
gCondition = threading.Condition()
gTimes = 0
gTotalTimes = 10

class Producer(threading.Thread):
    def run(self):
        global gMoney
        global gTimes
        global gTotalTimes
        while True:
            money = random.randint(100,1000)
            gCondition.acquire()
            if gTimes >= gTotalTimes:
                gCondition.release()
                break

            gMoney += money
            gTimes += 1
            print("%s生产了%d的钱,现在总共有%d" % (threading.current_thread(), money, gMoney))
            gCondition.notify_all() #通知wait等待的线程
            gCondition.release()
            time.sleep(1)


class Consumer(threading.Thread):
    def run(self):
        global gMoney
        while True:
            money = random.randint(100,1000)
            gCondition.acquire()

            while gMoney < money: 
            # 被等待的线程被唤醒后,又去线程队列中排队,但是排到的时候钱又不足了
                if gTimes > gTotalTimes:
                    gCondition.release()
                    return #直接跳出两个 while 循环
                
                print("%s,准备消费%d,剩余金额%d,金额不足,继续等待!!!" % (threading.current_thread, money, gMoney))
                gCondition.wait()

            gMoney -= money
            print("消费者%s,消费了%d,剩余金额%d"%(threading.current_thread,money,gMoney))
            gCondition.release()
            time.sleep(1)

def main():
    for x in range(2):
        t = Producer(name="生产者%d"%x)
        t.start()

    for x in range(3):
        t = Consumer(name=消费者%d%x)
        t.start()


if __name__ == __main__:
    main()

 

# Queue线程安全队列   线程安全的时候就不用加锁了
‘‘‘
在线程中,访问一些全局变量,加锁是一个经常的过程. 如果想把一些数据存储到某个队列中,那么python内置了一个线程安全的模块叫做queue模块.
python中的queue模块提供了同步的,线程安全的队列类,包括FIFO先进先出队列Queue,
LIFO后进先出队列LifeQueue.
这些队列实现了锁原理(可以理解为原子操作,要么都改,要么都不改),能够在多线程中直接使用,可以使用队列实现线程间的同步.

相关函数:
1.初始化 Queue(maxsize)创建一个先进先出的队列
2.qsize() 返回队列的大小,有多少个元素
3.empty() 判断队列是否为空
4.full() 判断队列是否满
5.get() 从队列中获取最后一个
6.put() 将一个数据放到队列中

q.get(block=True) 如果队列当中没有值就一直阻塞在这里,默认是True
q.put(block=True) 表示如果给队列添加值,如果队列是满的就一直等待着
‘‘‘

from queue import Queue

q = Queue(3)
q.put(2)
q.put(1)
q.put(3)

print(q.qsize()) # 3
print(q.full())  # true
print(q.empty()) # false
print(q.get())   # 2
##########################################
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():
    q = Queue(4)
    t1 = threading.Thread(target=set_value,args=[q])
    t2 = threading.Thread(target=get_value,args=[q])
    t1.start()
    t2.start()

if __name__ == __main__:
    main()

# set_value是每隔3秒产生一个
# get_value是一直在取,如果队列中没有就等着

 

以上是关于多线程的主要内容,如果未能解决你的问题,请参考以下文章

什么是多线程,多进程?

多线程和多进程模式有啥区别

多线程Java多线程学习笔记 | 多线程基础知识

java中啥叫做线程?啥叫多线程?多线程的特点是啥

c++ 多线程与c多线程有啥区别?

IOS多线程安全(线程锁)