第七章:

并发、并行和多线程

序言:并行是解决并发的一种途径

并发:concurrency;同时做某些事,但是强调,一个时段内的所有事情要处理
例如:同一时间段内大量的用户访问处理

并行:paraller;同时做某些事,可以互不干扰的同一时刻做几件事
例如:同一时间有多台服务器一起处理并发的用户访问

并发的解决:
    *队列、缓冲区
        Queue:解耦、缓冲

    *争抢
        谁抢到资源就上锁,排他性的锁
        优点:争抢也是一种高并发解决方案
        缺点:有可能有人很长时间抢不到

    *预处理:一种提前加载用户需要的数据的思路
        预处理思想,缓存常用

    *并行:水平扩展思想;如增加服务器、多开进程、线程实现并行处理
        单cpu:线程在单cpu就不是并行;cpu同一时刻只能运行一个线程

    *提速:垂直扩展
        例如:提高单个CPU性能,或单个服务器安装更多的CPU

    *消息中间件:
        第三方软件:RabbitMQ、ActiveMQ、RocketMQ、kafka

进程和线程:
线程:在实现了线程的操作系统中,线程是操作系统能够进行运算调度的最小单位;
*线程被包含在进程中,是进程中的实际运作单位
*一个程序的执行实例就是一个进程

进程:计算机中程序关于某数据集合上的一次运行活动;
    *是系统进行资源分配和调度的基本单位
    *是操作系统结构的基础


    进程和程序的关系:
        *程序是源代码编译后的文件,而这些文件存放在磁盘上。
        *当程序被操作系统加载到内存中,就是进程,进程中存放着指令和数据,它也是线程的容器
        *程序是指令和数据的集合;而进程是程序运行在内存中的一个实例

线程和进程:
    进程:
    1,每一个进程都认为自己独占所有的计算机硬件资源;
    2,一个进程无法直接访问另一个进程的变量和数据结构
    3,一个进程可以有多个线程

    线程:
    1,同一个进程内的多个线程会共享部分状态
    2,多个线程可以读写同一块内存(进程不能)
    3,每个线程拥有自己独立的寄存器和栈

    *进程切换开销大,线程切换开销小;进程间通信开销大,线程间通信开销小;
    *线程属于进程,不能独立执行。每个进程至少要有一个线程,成为主线程

线程的状态:
    就绪(Ready):线程能够运行,但在等待被调度。
                *可能线程刚刚创建启动,或刚刚从阻塞状态恢复,或者被其他线程抢占

    运行(Running):线程正在运行

    阻塞(Blocked):线程等待外部事件发生而无法运行,如I/O操作

    终止(Terminated):线程完成,或退出,或被取消


    Python中的进程和线程:
        进程会启动一个解释器进程,线程共享一个解释器进程;

    线程模块:threading
        threading的属性和方法:
            current_thread():返回当前线程对象

            main_thread():返回主线程对象

            active_count():当前处于alive活动状态的线程个数(包含主线程)

            enumerate():返回所有活动状态的线程列表,不包括已经终止的线程和未开始的线程(包含主线程)

            get_ident():返回当前线程的ID,非0整数

        Thread类:
            创建一个线程对象:
                例如:threading.Thread(target=add, name='s1', args=(4, 5))
                    target:线程调用的对象,就是需要执行的函数

                    name:为线程起个名字

                    args:为目标函数传递实参,是一个元组

                    kwargs:为目标函数关键字传参,是一个字典

            Thread实例的属性和方法:
                name:一个标识,可以重名;getName()、setName()获取、设置这个名字

                ident:线程ID,是非0整数。线程启动后才会有ID,否则为None。线程退出,此ID依旧可以访问,此ID可以重复使用
                    *ID必须唯一,但可以在线程退出后再分配给其他线程使用

                is_alive():返回线程是否活着

                start():启动线程;每一个线程必须且只能执行该方法一次,有个开关量is_set()
                    *start()会调用run()
                        1,先执行 _start_new_thread(self._bootstrap, ())
                        2,self._bootstrap调用self._bootstrap_inner()
                        3,self._bootstrap_inner()中执行self.run()

                    *同一个线程对象只能start一次,第二会抛出运行时异常
                        解释:在创建一个线程对象时,有一个 self._started = Event()
                                1,Event()这个实例有一个实例属性self._flag = False
                                2,当start执行启动一个新的线程: _start_new_thread(self._bootstrap, ())
                                3,实例的_bootstrap方法执行实例的_bootstrap_inner()
                                4,在实例的_bootstrap_inner()中执行了self._started.set()中的self._flag = True
                                5,当前实例的self._started.is_set()属性值是True保存在这个实例对象中
                                6,下一次调用这个实例start方法,会判断self._started.is_set()为真就抛出raise异常!
                                    if self._started.is_set():
                                        raise RuntimeError("threads can only be started once")

                run():run()方法可以运行函数

                run()和start()区别:
                    run():运行在当前主线程上,没有创建新的线程

                    start():先创建一个新的线程,在调用run()运行函数在这个新的线程上压栈


                join方法:使用了join方法后,daemon线程执行完,主线程才退出
                    join(timeout=None):是线程的标准方法之一
                    作用:一个线程中调用另一个线程的join方法,调用者将被阻塞,直到被调用线程终止或执行完。
                        *一个线程可以被join多次
                        *timeout参数指定调用者等待多久,没有设置超时,就一致等到被调用线程结束
                        *调用谁的join方法,就是join谁,就要等谁。

                        示例代码:
                            import time
                            import threading

                            def foo(n):
                                for i in range(n):
                                    print(i)
                                    time.sleep(1)
                            t1 = threading.Thread(target=foo, args=(10,), daemon=True)
                            t1.start()
                            t1.join()
                            print('---------')


        local类:threading.local类构建了一个大字典;
            {id(Thread) ---> (ref(Thread), thread-local dict)}
                *key:每一个线程实例的地址
                *value:vlaue是一个元组;元组的第一个元素是线程对象,第二个元素是每个线程自己的字典

                示例代码:
                    import threading
                    import time

                    global_data = threading.local()

                    def worker():
                        global_data.x = 100
                        for i in range(100):
                            time.sleep(0.01)
                            global_data.x += 1
                        print(threading.current_thread(), global_data.x)

                    for i in range(10):
                        threading.Thread(target=worker).start()


        Timer类(定时器):延迟执行
            格式:def __init__(self, interval, function, args=None, kwargs=None):
            *threading.Timer继承自Thread,这个类用来定义多久执行一个函数;
            *执行start()方法之后,Timer对象会处于等待状态,等待了interval之后,开始执行function函数
                *如果在执行函数之前的等待阶段,使用了cancel方法,就会跳过执行函数结束。

                    示例代码:
                        import threading
                        import logging
                        import time

                        FORMAT = '%(asctime)s %(threadName)s %(thread)d %(message)s'
                        logging.basicConfig(format=FORMAT, level=logging.INFO)


                        def worker():
                            logging.info('in worker')
                            time.sleep(2)

                        th = threading.Timer(5, worker)
                        th.setName('thname')
                        th.start() # 启动线程
                        print(threading.enumerate())
                        th.cancel() # 取消,必须在函数执行之前
                        time.sleep(1)
                        print(threading.enumerate())

            Timer类总结:
                *Timer是线程Thread的子类,具有线程的能力和特征!
                *Timer类的实例能够延时执行目标函数的线程,在真正执行目标函数之前,都可以cancel()         


        ***Lock类:锁
            作用:凡是存在共享资源争抢的地方都可以使用锁,从而保证只有一个使用者可以完全使用这个资源

                方法:
                    acquire(blocking=True, timeout=-1):默认为阻塞,阻塞可以设置超时时间。
                        *非阻塞时,timeout禁止设置;成功获得锁,返回True,否则返回False
                    release():释放锁,可以从任何线程调用释放
                        *已经上锁的锁,会被重置为unlocked状态
                        *未上锁的锁上调用,抛出RuntimeError异常

                锁的应用场景:
                    *少用锁,必要时用锁,使用了锁,多线程访问被锁的资源时,就成了串行,排队执行或者争抢执行
                    *加锁时间越短越号,不需要立即释放锁
                    *一定要避免死锁


                    问题:一般,加锁就需要解锁,但是加锁后解锁前,还要执行一些代码,就有可能抛出异常,一旦出现异常,锁时无法释放,但是当前线程可能因为这个异常被终止了,这样就产生了死锁
                        加锁、解锁常用语句:
                            1,使用try...finally语句保证锁的释放
                            2,with上下文管理,锁对象支持上下文管理

                        示例代码:
                            import threading
                            from threading import Thread, Lock, Event
                            import time

                            class Counter:
                                def __init__(self):
                                    self._val = 0
                                    self.__lock = Lock()

                                @property
                                def value(self):
                                    with self.__lock:
                                        return self._val

                                def inc(self):
                                    try:
                                        self.__lock.acquire()
                                        self._val += 1
                                    finally:
                                        self.__lock.release()

                                def dec(self):
                                    with self.__lock:
                                        self._val -= 1

                            def run(c, count=100):
                                for _ in range(count):
                                    for i in range(-50, 50):
                                        if i < 0:
                                            c.dec()
                                        else:
                                            c.inc()


                            c = Counter()
                            c1 = 10
                            c2 = 1000


                            for i in range(c1):
                                Thread(target=run, args=(c, c2)).start()

                            while True:
                                time.sleep(1)
                                if threading.active_count() == 1:
                                    print(threading.enumerate())
                                    print(c.value)
                                    break



                问题:10个工人一起生成1000个杯子
                    存在的问题:多生产杯子。当前线程还没有提交杯子,就被切换到另外一个线程了。
                    示例代码:
                        from threading import Thread, Lock, current_thread
                        import logging
                        import time


                        FORMAT = '%(asctime)s %(threadName)s %(thread)d %(message)s'
                        logging.basicConfig(format=FORMAT, level=logging.INFO)

                        cups = [] # 全局变量,共享资源

                        def worker(count=10):
                            logging.info("I'm working for U.")
                            while len(cups) < count:
                                time.sleep(1)
                                cups.append(1)
                            logging.info('I finished. cups={}'.format(len(cups)))

                        for _ in range(10):
                            Thread(target=worker, args=(100,)).start()

                    使用Lock锁:
                        import threading
                        from threading import Thread, Lock
                        import logging
                        import time

                        FORMAT = '%(asctime)s %(threadName)s %(thread)d %(message)s'
                        logging.basicConfig(format=FORMAT, level=logging.INFO)

                        cups = []
                        lock = Lock()

                        def worker(count=10):
                            logging.info("I'm working for U.")
                            flag = False
                            while True:
                                lock.acquire() # 获取锁
                                if len(cups) >= count:
                                    flag = True
                                time.sleep(1)
                                if not flag:
                                    cups.append(1)
                                lock.release()
                                if flag:
                                    break
                            logging.info('I finished. cups={}'.format(len(cups)))

                        for _ in range(10):
                            Thread(target=worker, args=(10,)).start()



        ***可重入锁RLock:
            *可重入锁,是线程相关的锁
            *可重入锁,可在一个线程中获取锁,并可以继续在同一线程中不阻塞获取锁;当锁未释放完,其他线程获取锁就会阻塞,直到当前持有锁的线程释放完锁。


        Condition类:
        作用:Condition用于生产者,消费者模型,为了解决生产者消费者速度匹配问题。
            构造方法Condition(lock=None),可以传入一个Lock或RLock对象;默认是RLock
                acquire(*args):获取锁
                wait(self, timeout=None):等待或超时
                notify(n=1):唤醒至多指定数目个数的等待的线程,没有等待的线程就没有任何操作
                notify_all():唤醒所有等待的线程

                问题:生产者和消费者速度匹配问题?
                    示例代码:
                        from threading import Thread, Event, Condition
                        import logging
                        import random

                        FORMAT = '%(asctime)s %(threadName)s %(thread)d %(message)s'
                        logging.basicConfig(format=FORMAT, level=logging.INFO)

                        class Dispather:
                            def __init__(self):
                                self.data = None
                                self.event = Event()
                                self.cond = Condition()

                            def produce(self, total):
                                for _ in range(total):
                                    data = random.randint(0, 100)
                                    with self.cond:
                                        logging.info(data)
                                        self.data = data
                                        self.cond.notify_all()
                                    self.event.wait(1)
                                self.event.set()

                            def consume(self):
                                while not self.event.is_set():
                                    with self.cond:
                                        self.cond.wait()
                                        logging.info("receive {}".format(self.data))
                                        self.data = None
                                    self.event.wait(0.5)

                        d = Dispather()
                        p = Thread(target=d.produce, args=(10,), name='produce')
                        c = Thread(target=d.consume, name='consume')
                        c.start()
                        p.start()

            Condition总结:
                机制:
                    *采用了通知机制,非常有效率
                    *消费者wait(),等待通知
                    *生产者生产完成,对消费者发送通知,可以使用notify或者notify_all方法

                作用:
                    *可以用于生产者和消费者模型中,解决生产者消费者速度匹配的问题

                使用方式:
                    使用Condition,必须先acquire(加锁),用完要release(释放锁),因为内部使用了锁,默认使用RLock锁
                    *使用with上下文


        线程启动:对线程对象执行start()方法
            例如:threading.Thread(target=add, name='s1', args=(4, 5)).start()

            *通过threading.Thread创建一个线程对象,target是目标函数,name可以指定名称;现在只是创建一个线程对象,这个对象并没有真正在内存中运行
            *调用start方法,就可以让线程运行
            重点:线程之所以执行函数,是因为线程中就是执行代码的,而最简单的封装就是函数,所以还是函数调用。函数执行完,线程也就退出了

        Python中的线程退出:
            Python没有提供线程退出的方法:
                *线程函数内语句执行完毕,没有需要处理的,正常退出
                *线程函数中抛出未处理的异常
                *Python中的线程没有优先级、没有线程组的概念,也不能被销毁、停止、挂起、中断、恢复


        多线程:一个进程中有多个线程,就是多线程,实现一种并发!
            *当使用start方法启动线程后,进程内有多个活动的线程并行的工作,就是多线程
            *一个进程中至少有一个线程,并作为程序的入口,这个线程就是主线程,一个进程至少有一个主线程,其他线程成为工作线程。
            问题:因为print函数是线程不安全的。
            示例代码:
                import threading
                import time

                def worke():
                    count = 0
                    while True:
                        if count > 5:
                            break
                        time.sleep(3)
                        count += 1
                        print('worker running')
                        print(threading.current_thread().name, threading.current_thread().ident)
                        print('+++++++++++')

                class MyThread(threading.Thread):
                    def start(self):
                        print('start-----------')
                        super().start()

                    def run(self):
                        print('run-------------')
                        super().run()

                t1 = MyThread(name='worker1', target=worke)
                t2 = MyThread(name='worker2', target=worke)

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

        线程安全:
            问题:print函数被打断了,被线程切换打断了。
                1,print函数分为两步,第一步打印字符串
                2,第二步打印换行,就在这之间,发生了线程的切换,换行还没有打印之前,切换到了其他线程

                线程安全:
                    线程执行一段代码,不会产生不确定的结果,这段代码就是线程安全的。

        ***使用logging模块:见模块篇***


        daemon线程和non—daemon线程:
            主线程是第一个启动的线程;
            父线程:如果线程A中启动了一个线程B,A就是B的父线程
            子线程:B就是A的子线程

            daemon:表示线程是否是daemon线程,这个值必须在start()之前设置,否则引发RuntimeError异常
            isDaemon():是否是daemon线程
            setDaemon:设置为daemon线程,必须在start()方法之前设置完成

            应用场景:
                1,后台任务;如发送心跳包、监控
                2,主线程工作才有用的线程,如主线程中维护着公共的资源,主线程已经清理了,准备退出,而工作线程使用这些资源工作也没有意义了,一起退出最合适
                3,随时可以被终止的线程

            *构造线程时,可以设置daemon属性,这个属性必须在start方法前设置完成。
            def __init__(self, group=None, target=None, name=None, args=(), kwargs=None, *, daemon=None):
                        if daemon is not None:
                            self._daemonic = daemon
                        else:
                            self._daemonic = current_thread().daemon

            *如果未设置daemon属性,默认为None,取当前线程的daemon值
                Fasle:non-daemon线程;如果有non-daemon线程,主线程退出时,也不会杀掉所有daemon线程,直到所有non-daemon线程全部结束
                True:daemon线程;如果有daemon线程时,主线程需要退出时,会结束所有daemon线程,结束退出。
                    示例代码:
                        import threading
                        import time

                        def bar():
                            time.sleep(3)
                            print('bar')

                        def foo():
                            for i in range(20):
                                print(i)
                            t = threading.Thread(target=bar, daemon=False)
                            t.start()

                        t = threading.Thread(target=foo, daemon=True)
                        t.start()
                        time.sleep(2)
                        print("Main Thread Exiting")

线程同步:
    线程间协同,通过某种技术,让一个线程访问某些数据时,其他线程不能访问这些数据,直到该线程完成对数据的操作。

    ***Event类:
        Event事件,是线程间通信机制中最简单的实现,使用一个内部的标记flag,通过flag为True或False的变化来进行操作。

        set():标记设置为True
        clear():标记清除,设置为False
        is_set():标记是否为True
        wait(timeout=None):设置等待标记为True的时长,None为无限等待;等到返回True,未等到超时了返回False

    问题:一个老板,一个员工,员工需要生产100个杯子
        示例代码:
            from threading import Event, Thread
            import logging
            import time


            FORMAT = '%(asctime)s %(threadName)s %(thread)d %(message)s'
            logging.basicConfig(format=FORMAT, level=logging.INFO)


            def worker(event, count=10):
                time.sleep(1)
                logging.info('------this start------')
                cups = []
                while True:
                    logging.info('make1')
                    time.sleep(0.5)
                    cups.append(1)
                    if len(cups) >= count:
                        event.set()
                        break

                logging.info('finished. cups={}'.format(cups))


            def boss(event):
                logging.info('start-------')
                event.wait()
                logging.info('stop--------')

            event = Event()
            w = Thread(target=worker, args=(event,))
            b = Thread(target=boss, args=(event,))
            w.start()
            b.start()

            总结:
                *使用同一个Event()实例对象的标记flag
                *谁wait就是等到flag标记变为True,或等到超时时返回False
                *同一个Event()实例对象的wait()可以被多个使用。

        wait():默认为空,永久等待;如果给值就等待这个值
            优点:Event的wait()优于time.sleep(),它会更快的切换到其他线程,提高并发效率

            示例代码:
                from threading import Event, Thread
                import logging
                logging.basicConfig(level=logging.INFO)

                def do(event, interval):
                    while not event.wait(interval):
                        logging.info('do something.')

                event = Event()
                Thread(target=do, args=(event, 3)).start()
                event.wait(10)
                event.set()
                print('main exit')

            问题:自己实现Timer类
                示例代码:
                    from threading import Event, Thread
                    import datetime
                    import logging


                    FORMAT = '%(asctime)s %(threadName)s %(thread)d %(message)s'
                    logging.basicConfig(format=FORMAT, level=logging.INFO)


                    class Timer:
                        def __init__(self, interval, func, *args, **kwargs):
                            self.interval = interval
                            self.func = func
                            self.args = args
                            self.kwargs = kwargs
                            self.event = Event()

                        def start(self):
                            Thread(target=self.run).start()

                        def cancel(self):
                            self.event.set()

                        def run(self):
                            start = datetime.datetime.now()
                            logging.info('waiting')

                            if not self.event.wait(self.interval):
                                self.func(*self.args, **self.kwargs)
                            data = (datetime.datetime.now() - start).total_seconds()
                            logging.info('finished {}'.format(data))

                            self.event.set()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值