python爬虫八:多任务线程

1、多线程

①举例:有很多的场景中的事情是同时进行的,比如开车的时候 手和脚共同来驾驶汽车,再比如唱歌跳舞也是同时进行的
②定义:线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。线程间通信主要通过共享内存,上下文切换很快,资源开销较少,但相比进程不够稳定容易丢失数据。

1.1 模拟多任务

import time
def sing():
    for i in range(3):
        print("正在唱歌...%d"%i)
        time.sleep(1)
def dance():
    for i in range(3):
        print("正在跳舞...%d"%i)
        time.sleep(1)
if __name__ == '__main__':
    sing()
    dance()
    # 只有当唱歌结束了,跳舞才开始,为了让他们同时进行所以有了线程

2、主线程和子线程的执行关系

主线程会等待子线程结束之后在结束
join() 等待子线程结束之后,主线程继续执行
setDaemon() 守护线程,不会等待子线程结束

# 通过函数 使用threading模块中的 Thread类来创建线程
# 通过类 继承threading模块中的 Thread类并实现run()方法
import threading
import time

def demo():
    for i in range(5):
        print('hello 子线程') # 子线程
        time.sleep(1)
if __name__ == '__main__':
        # 参数一 group 线程组 None
        # 参数二 target 要执行的方法
        # 参数三 name 线程名
    t = threading.Thread(target=demo) # 创建线程? 面试题
    # 守护线程 不会等待子线程结束
    # t.setDaemon(True)
    # 主线程会等待子线程执行结束之后,主线程在结束
    t.start() # 开启线程
    # 等待子线程结束之后,后面的主线程在去继续执行,不与子线程抢资源
    t.join()
    # time.sleep(6)
    # print(1)

3、查看线程数量

threading.enumerate() 查看当前线程的数量

复制代码可以观看线程数量,实践方法

import threading
import time

def demo1():
    for i in range(5):
        print('demo1...%d'%i)
        time.sleep(2)
def demo2():
    for i in range(10):
        print('demo2...%d' % i)
        time.sleep(2)
def main():
    t1 = threading.Thread(target=demo1, name='demo1')
    t2 = threading.Thread(target=demo2, name='demo2')
    t1.start()
    t2.start()
    # t2.join()
    # t1.join()
    # while True:
    #     print(threading.enumerate())
    #     if len(threading.enumerate()) <= 1:
    #         break
    #     time.sleep(1)
if __name__ == '__main__':
    main()

4、验证子线程的执行与创建

4.1threading.thread

使用threading模块中的 Thread类来创建线程

import threading
import time

def demo():
    for i in range(5):
        print('demo..%d'%i)
        time.sleep(1)
def main():
    print(threading.enumerate()) # 一个主线程
    t1 = threading.Thread(target=demo,name='demo') # 创建? 证明它不是创建线程
    print(threading.enumerate()) # 2 or 1 只有一个主线程
    t1.start() #   创建线程并启动线程
    print(threading.enumerate()) # 2 or 1 有2个线程 一个是主线程一个是子线程
if __name__ == '__main__':
    main()

4.2继承Thread类重写run()方法

import threading
import time

class A(threading.Thread):

    def run(self):
        for i in range(5):
            print(i)
if __name__ == '__main__':
    a = A()
    print(threading.enumerate()) # [<_MainThread(MainThread, started 9320)>]
    time.sleep(3)
    a.start()
    print(threading.enumerate()) # [<_MainThread(MainThread, started 9320)>, <A(Thread-1, started 1856)>]
    time.sleep(3)
    print(threading.enumerate()) # [<_MainThread(MainThread, started 7780)>]

5、线程间的通信(多线程共享全局变量)

5.1多线程参数—args

import time
import threading

num = [1,2]
def demo1(nums):
    num.append(nums)
    print('demo1中的num%s'%str(num))
def demo2():
    print('demo2中的num%s'%str(num))
def main():
    t1 = threading.Thread(target=demo1,args=(10,))
    t2 = threading.Thread(target=demo2)
    t1.start()
    t1.join()
    t2.start()
if __name__ == '__main__':
    main()

5.2线程共享全局变量

# a = 20
# def fn():
#     global a
#     a = 10
#     print('函数内部a:%d'%a)#10
#
# print('函数外部a:%d'%a)#20
# fn()
# print('函数外部a:%d'%a)#10

import threading
import time

num = 100
def demo1():
    global num
    num += 1
    print('demo1 - num:%d'%num)
def demo2():
    print('demo2 - num:%d'%num)
def main():
    t1 = threading.Thread(target=demo1)
    t2 = threading.Thread(target=demo2)
    t1.start()
    t1.join()
    t2.start()
if __name__ == '__main__':
    main()
    #结果
    #demo1 - num:101
    #demo2 - num:101

5.3线程之间的资源竞争

import threading
import time

num = 0
def demo1(nums):
    global num
    for i in range(nums):
        num += 1
    print('demo1中的 num:%d'%num)
def demo2(nums):
    global num
    for i in range(nums):
        num += 1
    print('demo2中的 num:%d'%num)
def main():
    t1 = threading.Thread(target=demo1,args=(100,))
    t2 = threading.Thread(target=demo2,args=(100,))
    t1.start()
    #t1.join()
    t2.start()
    # demo1中的 num:100
    # demo2中的 num:200
    # t2.start()
    # t1.start()
    #demo2中的 num:100
    #demo1中的 num:200
if __name__ == '__main__':
    main()
    
#import dis # 可以查看cpu的运行轨迹
#def fn(num):
#    num += 1
#print(dis.dis(fn))

6、互斥锁和死锁

当多个线程几乎同时修改某一个共享数据的时候,需要进行同步控制
某个线程要更改共享数据时,先将其锁定,此时资源的状态为"锁定",其他线程不能改变,只到该线程释放资源,将资源的状态变成"非锁定",其他的线程才能再次锁定该资源。互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性。

6.1互斥锁(mutex)

import threading
import time

mutex = threading.RLock() # 创建锁,默认是没有上锁的,可重复的锁可创建多次,但是要注意解锁
# mutex = threading.Lock() # 创建锁,默认是没有上锁的,不可重复的锁只能创建一次
num = 0
def demo1(nums):
    global num
    mutex.acquire() # 获得互斥锁,锁定上锁
    mutex.acquire() # 获得互斥锁,锁定上锁上双锁可以用Rlock但要注意解锁
    for i in range(nums):
        num += 1
    print('demo1中的 num:%d'%num)
    mutex.release() # 解锁
    mutex.release() # 解锁
def demo2(nums):
    global num
    mutex.acquire()
    for i in range(nums):
        num += 1
    print('demo2中的 num:%d'%num)
    mutex.release()
def main():
    t1 = threading.Thread(target=demo1,args=(1000000,))
    t2 = threading.Thread(target=demo2,args=(1000000,))
    t1.start()
    t2.start()
if __name__ == '__main__':
    main()

6.2死锁

在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁。

import threading
import time

class MyThread1(threading.Thread):
    def run(self):
        # 对mutexA上锁
        mutexA.acquire()
        # mutexA上锁后,延时1秒,等待另外那个线程 把mutexB上锁
        print(self.name+'----do1---up----')
        time.sleep(1)
        # 此时会堵塞,因为这个mutexB已经被另外的线程抢先上锁了
        mutexB.acquire()
        print(self.name+'----do1---down----')
        # mutexB.release()
        mutexA.release()
        # 对mutexA解锁
        mutexB.release()
        # mutexA.release()

class MyThread2(threading.Thread):
    def run(self):
        # 对mutexB上锁
        mutexB.acquire()
        # mutexB上锁后,延时1秒,等待另外那个线程 把mutexA上锁
        print(self.name+'----do2---up----')
        time.sleep(1)
        # 此时会堵塞,因为这个mutexA已经被另外的线程抢先上锁了
        mutexA.acquire()
        print(self.name+'----do2---down----')
        mutexA.release()
        # 对mutexB解锁
        mutexB.release()

mutexA = threading.Lock()
mutexB = threading.Lock()

if __name__ == '__main__':
    t1 = MyThread1()
    t2 = MyThread2()
    t1.start()
    t2.start()

6.3避免死锁

程序设计时要尽量避免
添加超时时间等

7、Queue线程

from queue import Queue

q = Queue(3) # 初始化Queue(maxsize指定大小):创建一个先进先出的队列
print(q.empty()) # True空的队列 False 队列不是空的 empty():判断队列是否为空
print(q.full()) # False队列不是满的 True队列是满的 full():判断队列是否满了
q.put(1)
q.put(2)
q.put(3) # 多存或者多取都会阻塞
# q.put(4,timeout=2)
# q.put_nowait(4)
print(q.get()) # 多存或者多取都会阻塞
print(q.get())
print(q.get())
# print(q.get(timeout=2))
print(q.get_nowait())
print(q.full())

8、线程同步

'''
天猫精灵:小爱同学
小爱同学:在
天猫精灵:现在几点了?
小爱同学:你猜猜现在几点了
第一步:mutex解决不了
第二步:用condition解决
'''
# import threading
# import time
#
# class TianMao(threading.Thread):
#     def __init__(self,lock):
#         self.lock = lock
#         super().__init__(name='天猫精灵')
#     def run(self):
#         # self.lock.acquire()
#         print('{}:小爱同学'.format(self.name))
#         # self.lock.release()
#
#         # self.lock.acquire()
#         print('{}:现在几点了'.format(self.name))
#         # self.lock.release()
#
# class XiaoAi(threading.Thread):
#     def __init__(self, lock):
#         super().__init__(name='小爱同学')
#         self.lock = lock
#
#     def run(self):
#         print('{}:在'.format(self.name))
#         print('{}:你猜猜现在几点了'.format(self.name))
#
# if __name__ == '__main__':
#     mutex = threading.RLock
#     t = TianMao(mutex)
#     x = XiaoAi(mutex)
#     t.start()
#     x.start()
'''
第二步condition
'''
import threading
import time


class TianMao(threading.Thread):
    def __init__(self, cond):
        self.cond = cond
        super().__init__(name='天猫精灵')

    def run(self):
        self.cond.acquire()
        print('{}:小爱同学'.format(self.name))
        self.cond.notify()
        self.cond.wait()
        print('{}:现在几点了'.format(self.name))
        self.cond.notify()
        self.cond.release()

class XiaoAi(threading.Thread):
    def __init__(self, cond):
        super().__init__(name='小爱同学')
        self.cond = cond

    def run(self):
        # self.cond.acquire()
        with self.cond:
            self.cond.wait()
            print('{}:在'.format(self.name))
            self.cond.notify()
            self.cond.wait()
            print('{}:你猜猜现在几点了'.format(self.name))
            # self.cond.notify()
        # self.cond.release()
if __name__ == '__main__':
    cond = threading.Condition()
    t = TianMao(cond)
    x = XiaoAi(cond)
    x.start()
    t.start()
'''谁先等就要谁先运行(调用的顺序是先调用等待的线程),然后通知没等的去执行,有通知就要去找有等待的继续执行'''
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值