python 多线程

 

示例1

import threading
import time
# 进程比较消耗资源   线程比较消耗cpu


def func01(number):
    print("函数func01  start")
    time.sleep(2)
    print("函数func01  end")



def func02(number):
    print("函数func02  start")
    time.sleep(2)
    print("函数func02  end")



if __name__ == '__main__':
    # 创建两个线程 传入执行的函数
    thread01 = threading.Thread(target=func01,args=(1,))
    thread02 = threading.Thread(target=func02,args=(2,))

    begin_time = time.time()
    # 执行线程
    thread01.start()
    thread02.start()
    ent_time = time.time()

    print("耗时:",ent_time-begin_time)

 

线程守护

如果有一个线程必须设置为循环,那么该线程不结束,意味着整个python程序就不能结束,那为了能够让python程序正常退出,将这类循环的线程设置为守护线程,当程序当中仅仅剩下守护线程时,python程序就能够正常退出,不必关心这类线程是否执行完毕。

2、作用

守护线程作用是为其他线程提供便利服务,守护线程最典型的应用就是 GC (垃圾收集器)。

3、使用注意

(1)守护线程会在“该进程内所有非守护线程全部都运行完毕后,守护线程才会挂掉”

(2)主线程在其他非守护线程运行完毕后才算运行完毕(守护线程在此时就被回收)。因为主线程的结束意味着进程的结束,进程整体的资源都将被回收

子线程默认为【非守护线程】,主线程代码执行完毕,各子线程继续运行,直到所有【非守护线程】结束,python程序退出。

我们在对线程进行运行时,如果其中的子线程不结束,会导致整个程序不能停止。但如果在其中加入了守护线程就不一样了,从名称就可以看出是保护整个线程的,所以程序可以放心的退出了。

import threading
import time
# 进程比较消耗资源   线程比较消耗cpu


def func01(number):
    print("函数func01  start")
    time.sleep(2)
    print("函数func01  end")



def func02(number):
    print("函数func02  start")
    # 把线程2 设置等待4秒和 线程1做区分
    time.sleep(4)
    print("函数func02  end")



if __name__ == '__main__':
    # 创建两个线程 传入执行的函数
    thread01 = threading.Thread(target=func01,args=(1,))
    thread02 = threading.Thread(target=func02,args=(2,))

    begin_time = time.time()
    # 为线程2 设置守护线程 当主线程退出时 子线程thread02 同时也kill
    thread02.setDaemon(True)
    # 执行线程
    thread01.start()
    thread02.start()
    ent_time = time.time()

    print("耗时:",ent_time-begin_time)

 

主线程等待子线程完成,阻塞主线程

import threading
import time
# 进程比较消耗资源   线程比较消耗cpu


def func01(number):
    print("函数func01  start")
    time.sleep(2)
    print("函数func01  end")



def func02(number):
    print("函数func02  start")
    # 把线程2 设置等待4秒和 线程1做区分
    time.sleep(4)
    print("函数func02  end")



if __name__ == '__main__':
    # 创建两个线程 传入执行的函数
    thread01 = threading.Thread(target=func01,args=(1,))
    thread02 = threading.Thread(target=func02,args=(2,))

    begin_time = time.time()
    # 为线程2 设置守护线程 当主线程退出时 子线程thread02 同时也kill
    thread02.setDaemon(True)
    # 执行线程
    thread01.start()
    thread02.start()

    # 设置线程阻塞  只要当子线程全部执行完,在切到主线程上
    thread01.join()
    thread02.join()

    ent_time = time.time()

    print("耗时:",ent_time-begin_time)

 

 

用类的方法管理多线程

import threading
import time
# 进程比较消耗资源   线程比较消耗cpu


class Stu01(threading.Thread):

    def __init__(self,name):
        super().__init__(name=name)

    # 重载run方法 实现业务逻辑
    def run(self):
        print("类stu01  start")
        time.sleep(2)
        print("类stu01  end")


class Stu02(threading.Thread):

    def __init__(self,name):
        super().__init__(name=name)

    # 重载run方法 实现业务逻辑
    def run(self):
        print("类stu02  start")
        time.sleep(2)
        print("类stu02  end")


if __name__ == '__main__':
    # 创建两个线程 传入执行的函数
    thread01 = Stu01("小米")
    thread02 = Stu02("小红")

    begin_time = time.time()
    # 为线程2 设置守护线程 当主线程退出时 子线程thread02 同时也kill
    thread02.setDaemon(True)
    # 执行线程
    thread01.start()
    thread02.start()

    # 设置线程阻塞  只要当子线程全部执行完,在切到主线程上
    thread01.join()
    thread02.join()

    ent_time = time.time()

    print("耗时:",ent_time-begin_time)

 

线程间通信 

共享变量

线程不太安全


import threading
import time
# 进程比较消耗资源   线程比较消耗cpu

# 线程间通信方式 共享变量
# 先定义一个全局变量
detail_url_list = []

def get_detail_html(detail_url_list):
    # 爬取文章详情页
    # global detail_url_list
    while True:
        if len(detail_url_list):
            url = detail_url_list.pop()
            print("函数func01  start")
            time.sleep(2)
            print("函数func01  end")



def get_deatil_url(detail_url_list):
    # 爬取文章列表页

    # global detail_url_list
    while 1:
        print("函数func02  start")
        time.sleep(4)
        for i in range(20):
            detail_url_list.append("http://projectsedu.com/{id}".format(id=i))
        print("函数func02  end")





if __name__ == '__main__':


    begin_time = time.time()

    thread_detail_url = threading.Thread(target=get_deatil_url,args=(detail_url_list,))

    for i in range(5):
        html_thread = threading.Thread(target=get_detail_html,args=(detail_url_list,))
        html_thread.start()
    
    thread_detail_url.start()
    
    ent_time = time.time()

    print("耗时:",ent_time-begin_time)

 

通过消息队列queue

import threading
import time
# 通过queue 消息队列 的方式进行线程间同步
from queue import Queue


# 线程间通信


def get_detail_html(queue):
    # 爬取文章详情页
    # global detail_url_list

    while True:
        # 是阻塞的,如果队列为空会停止等待
        url = queue.get()
        print("函数func01  start")
        time.sleep(2)
        print("函数func01  end")


def get_deatil_url(queue):
    # 爬取文章列表页

    # global detail_url_list
    while 1:
        print("函数func02  start")
        time.sleep(4)
        for i in range(20):
            queue.put("http://projectsedu.com/{id}".format(id=i))
        print("函数func02  end")


if __name__ == '__main__':
    begin_time = time.time()
    # 定义最大值
    detail_url_queue = Queue(maxsize=1000)
    thread_detail_url = threading.Thread(target=get_deatil_url, args=(detail_url_queue,))

    for i in range(5):
        html_thread = threading.Thread(target=get_detail_html, args=(detail_url_queue,))
        html_thread.start()

    thread_detail_url.start()

    # 阻塞 等待执行完成在切换到主线程
    detail_url_queue.task_done()
    detail_url_queue.join()

    ent_time = time.time()

    print("耗时:", ent_time - begin_time)

 

线程同步

加锁lock

from threading import Lock


# 线程间同步

# 定义一把锁
lock = Lock()

# 1、锁会有一定影响并发性能 ,因为获取锁和释放锁 都需要时间
# 2、锁会影响死锁
"""
A(a,b)
acquire(a) A线程加锁先获取a资源
acquire(b) 在获取b资源才能释放锁

B(b,a)
acquire(a) B线程加锁先获取b资源
acquire(b) 在获取a资源才能释放锁

# 这样交叉资源竞争等待对方释放锁 ,就会陷入死循环


在举例
def add(lock):

    global total
    for i in range(1000):
        lock.acquire()  #  获取锁
        dosomething(lock)  # 传入锁后 因为上了两次 里面等外面释放,而外面代码要执行 必须等里面结束 ,所以会死锁
        
        total += 1
        lock.release()  # 释放锁 如果不释放 其他代码块无法执行
        
def dosomething(lock):
    lock.acquire()
    print("do something")
    lock.release()

"""

total = 0

def add():

    global total
    global lock

    for i in range(1000):
        lock.acquire()  #  获取锁
        total += 1
        lock.release()  # 释放锁 如果不释放 其他代码块无法执行

def desc():

    global total
    global lock
    for i in range(1000):
        lock.acquire()  # 获取锁
        total -= 1
        lock.release()  # 释放锁


import threading
thread1 = threading.Thread(target=add)
thread2 = threading.Thread(target=desc)

thread1.start()
thread2.start()

thread1.join()
thread2.join()

print(total)

 

可重复锁RLock

from threading import Lock,RLock # 可重入锁

# 在同一个线程里面,可以连续调用多次acquire
# 一定要注意acquire 的次数要和release的次数相等

# 线程间同步

# 定义一把锁
lock = RLock()

# 1、锁会有一定影响并发性能 ,因为获取锁和释放锁 都需要时间


total = 0

def add():

    global total
    global lock

    for i in range(1000):
        lock.acquire()  #  获取锁
        lock.acquire()  #  获取锁
        total += 1
        lock.release()  # 释放锁 如果不释放 其他代码块无法执行
        lock.release()  # 释放锁

def desc():

    global total
    global lock
    for i in range(1000):
        lock.acquire()  # 获取锁
        total -= 1
        lock.release()  # 释放锁


import threading
thread1 = threading.Thread(target=add)
thread2 = threading.Thread(target=desc)

thread1.start()
thread2.start()

thread1.join()
thread2.join()

print(total)

 

条件锁condition

import threading


# 条件变量  针对复杂的线程同步


"""
天猫精灵: 小爱同学
小爱:在
天猫精灵:我们来对古诗吧
小爱:好啊
天猫精灵:我住长江头
小爱:君住长江尾
天猫精灵:日日思君不见君
小爱:共饮长江水
天猫精灵:此水几时休
小爱:此恨何时己
天猫精灵:只愿君心似我心
小爱:定不负相思意
"""

# 通过condittion 完成协同读诗


class XiaoAi(threading.Thread):

    def __init__(self,cond):
        super().__init__(name="小爱")
        self.cond =cond

    def run(self):
        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.wait()  # 等待 通知
            print("{}:君住长江尾".format(self.name))
            self.cond.notify()  # 通知 天猫精灵

            self.cond.wait()  # 等待 通知
            print("{}:共饮长江水".format(self.name))
            self.cond.notify()  # 通知 天猫精灵

            self.cond.wait()  # 等待 通知
            print("{}:此恨何时己".format(self.name))
            self.cond.notify()  # 通知 天猫精灵

            self.cond.wait()  # 等待 通知
            print("{}:定不负相思意".format(self.name))
            self.cond.notify()  # 通知 天猫精灵



class TianMao(threading.Thread):

    def __init__(self, cond):
        super().__init__(name="天猫精灵")
        self.cond = cond

    def run(self):

        with self.cond:
            print("{} :小爱同学".format(self.name))
            self.cond.notify()  # 通知 小爱
            self.cond.wait() # 等待 小爱通知

            print("{} :我们来对古诗吧".format(self.name))
            self.cond.notify()  # 通知 小爱
            self.cond.wait()  # 等待小爱通知

            print("{} :我住长江头".format(self.name))
            self.cond.notify()  # 通知 小爱
            self.cond.wait()  # 等待小爱通知

            print("{} :日日思君不见君".format(self.name))
            self.cond.notify()  # 通知 小爱
            self.cond.wait()  # 等待小爱通知

            print("{} :此水几时休".format(self.name))
            self.cond.notify()  # 通知 小爱
            self.cond.wait()  # 等待小爱通知

            print("{} :定不负相思意".format(self.name))
            self.cond.notify()  # 通知 小爱
            self.cond.wait()  # 等待小爱通知

            print("{} :只愿君心似我心".format(self.name))
            self.cond.notify()  # 通知 小爱
            self.cond.wait()  # 等待小爱通知

if __name__ == '__main__':

    cond = threading.Condition()
    xiaoai = XiaoAi(cond)
    tianmao = TianMao(cond)

    # 启动顺序很重要
    # 在调用with cond之后才能调用wait或者notfy方法
    # condition 有两层锁,一把底层锁会在线程调用了wait方法的时候释放
    # 上面的锁会在每次调用wait的时候分配一把并放入到cond的等待队列中,等待notify方法的唤醒
    xiaoai.start()
    tianmao.start()

 

Semaphore 控制并发数量

import threading
import time

# 用于控制进入数量的锁

class HtmlSpider(threading.Thread):

    def __init__(self,url,sem):
        super().__init__()
        self.url = url
        self.sem =sem


    def run(self):
        time.sleep(2)
        print("got html text success")
        self.sem.release()


class UrlProducer(threading.Thread):

    def __init__(self,sem):
        super().__init__()
        self.sem = sem

    def run(self):
        for i in range(20):
            self.sem.acquire()
            html_thread = HtmlSpider("http://baidu.com/{}".format(i),self.sem)
            html_thread.start()

if __name__ == '__main__':

    # 控制并发次数
    sem = threading.Semaphore(3)
    url_producer = UrlProducer(sem)
    url_producer.start()

 

posted @ 2022-11-10 22:03  钟鼎山林  阅读(150)  评论(0)    收藏  举报