Python爬虫第六天:进程-线程安全-互斥锁-同步异步-队列-生产者消费者模型-协程

内容简述:

     一:初始线程和进程

     二:进程和线程的区别

     三:线程的调用方式

     四:线程安全与线程锁

     五:同步和异步

     六:队列

     七:生产者和消费者模型

     八:协程

一:初始线程,进程

       看图说话:

      多任务: 操作系统可同时运行多个任务,每一个程序内存都是独立的     

        一边浏览器上网,一边看电影,一边打开WORD至少3个任务在运行还有      后台运行的任务   概述:对于操作系统来说,一个任务就是一个进程.例如:打开一个浏览器就是启动一个浏览器进程,打开两word就启动了两个word进程,有的进程还不止同时干一件事,比如Word,它可以同时进行打字、拼写检查、缩进等事情。在一个进程内部,要同时干多件事,就需要同时运行多个“子任务”,我们把进程内的这些“子任务”称为线程(Thread)

      进程

       (Process)是一个正在执行的程序,以一个整体的形式暴露给操作系统管理,里面包含对各种资源的调用,内存的管理,网络接口的调用等。。。对各种资源管理的集合。所有在同一个进程里的线程是共享同一块内存空间的

      线程

        是进程中一个独立的控制单元,它被包含在进程之中。是操作系统最小的调度单位, 是一串指令的集合。 一个进程中至少有一个线程,一个进程中可以并发多个线程,每条线程并行执行不同的任务

      备注:

        进程要操作CPU,必须先创建一个线程,一个进程中至少有一个线程

二:进程和线程的区别

1-Threads share the address space of the process that created it; processes have their own address space.

线程共享创建它的进程的内存空间;进程有自己的内存空间是独立的

2-Threads have direct access to the data segment of its process; processes have their own copy of the data segment of the parent process.

线程可以直接访问进程的数据;进程拥有父进程的数据的副本

3-Threads can directly communicate with other threads of its process; processes must use interprocess communication to communicate with sibling processes.

同一个进程的线程之间可以互相访问;两个进程想通信,须通过一个中间代理来实现

4-New threads are easily created; new processes require duplication of the parent process.

创建新线程很容易;创建新进程需要复制父进程。

5-Threads can exercise considerable control over threads of the same process; processes can only exercise control over child processes.

一个线程可以控制和操作同一进程里的其他线程;进程只能操作子进程

6-Changes to the main thread (cancellation, priority change, etc.) may affect the behavior of the other threads of the process; changes to the parent process does not affect child processes.

对主线程的更改(取消、优先级更改等)可能会影响进程中其他线程的行为;对父进程的更改不会影响子进程。

三:线程的调用方式

        1-函数式调用

        2-继承式调用

四:线程安全与线程锁

        多线程中,所有变量都由所有线程共享。所以任何一个变量都可以被任意一个线程修改,因此,线程之间共享数据最大的危险在于多个线程同时修改一个变量,造成数据混乱问题。

        解决方式:线程锁(互斥锁)

   互斥锁

        当多个线程几乎同时修改某一个共享数据的时候,需要进行同步控制

        线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制是引入互斥锁。

        互斥锁为资源引入一个状态:锁定/非锁定。

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

        hreading模块中定义了Lock类,可以方便的处理锁定:

        lock= threading.Lock()      #创建锁

        lock.acquire([blocking])     #锁定

        lock.release()                   #释放

其中,锁定方法acquire可以有一个blocking参数。

如果设定blocking为True,则当前线程会堵塞,直到获取到这个锁为止(默认)

如果设定blocking为False,则当前线程不会堵塞

上锁解锁过程

当一个线程调用锁的acquire()方法获得锁时,锁就进入“locked”状态。每次只有一个线程可以获得锁。如果此时另一个线程试图获得这个锁,该线程就会变为“blocked”状态,称为“阻塞”,直到拥有锁的线程调用锁的release()方法释放锁之后,锁进入“unlocked”状态。线程调度程序从处于同步阻塞状态的线程中选择一个来获得锁,并使得该线程进入运行(running)状态。

小结:

    锁的优点:确保了某段关键代码只能由一个线程从头到尾完整地执行

    锁的缺点:

        1- 阻止了多线程并发执行,包含锁的某段代码实际上只能以单线程模式执行,效率降低。        

        2-由于可以存在多个锁不同的线程持有不同的锁,并试图获取对方持有锁时,可能会造成死锁

五:同步和异步

    同步:

            协同步调,按预定的先后次序进行运行。如:买票排队。

            多任务,多个任务之间执行的时候要求有先后顺序,必须一个先执行完成之后,另一个才能继续执行, 只有一个主线。简言之,同步意味着有序

    异步:

            一方的动作不用等另一方动作结果。如:不同窗口买票。

            多任务, 多个任务之间执行没有先后顺序,可以同时运行,执行的先后顺序不会有什么影响,存在的多条运行主线。简言之,异步意味着无序

六:队列

 1.队列:先进先出       栈:先进后出

    Python的Queue模块中提供了同步的、线程安全的队列类,包括FIFO(先进先出)队列Queue,LIFO(先进后出)队列LifoQueue,和优先级队列PriorityQueue。这些队列都实现了锁原语(原子性,即要么全做,要么全不做),能够在多线程中直接使用。可以使用队列来实现线程间的同步。

    相当于有顺序的容器,那为啥要有队列?

    列表和队列的根本区别是:取出数据后数据还在不在容器里。

    class queue.Queue(maxsize=0)                  #先进先出

    class queue.LifoQueue(maxsize=0)             #last in fisrt out  先进后出

    class queue.PriorityQueue(maxsize=0)       #存储数据时可设置优先级的队列

 2.Queue的简要说明

        put():添加数据到队列中

               queue.Queue.put(itemblock=Truetimeout=None)

        get():从队列中取数据

                queue.Queue.get(block=True,timeout=None)

        qsize():判断队列中是否有数据

        empty():判断队列是否为空。如果为空返回True

        full():如果满了返回True

        put_nowait(item)  相当于 toput(item,False)

        get_nowait()          相当于 toget(False).

        task_done()          任务执行完毕

3.队列的主要作用

        a.程序解耦

        b.提高效率

七:生产者消费者模型

        为啥要使用生产者和消费者模式?

        在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程。在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这个问题于是引入了生产者和消费者模式。

        什么是生产者消费者模式?

        生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。

八:协程

        协程:又称微线程,纤程。简单来讲:是一种用户态的轻量级线程

         协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈。基于这点:协程能保留上一次调用时的状态(即所有局部状态的一个特定组合),每次过程重入时,就相当于进入上一次调用的状态,换种说法:进入上一次离开时所处逻辑流的位置。

        优势:

                无需线程上下文切换的开销

                无需原子操作锁定及同步的开销

                方便切换控制流,简化编程模型

                高并发+高扩展性+低成本:一个CPU能支持上万的协程。

         缺点:

                无法利用多核资源:

                协程本质是个单线程,不能同时将单个CPU的多个核用上,需要和进程配合才能运行在多CPU上.除非cpu密集型应用日常所编写的绝大部分应用都没有这个必要。        

                进行阻塞(Blocking)操作(如IO时)会阻塞掉整个程序

          协程必备条件:

                a.必须在只有一个单线程里实现并发

                b.修改共享数据不需要加锁

                c.用户程序里自己保存多个控制流的上下文栈

                d.一个协程遇到IO操作自动切换到其它协程

            Greenlet:

                   greenlet是一个用C实现的协程模块,可以使你在任意函数间随意切换。

                   问题:上面的Greenlet遇到IO操作不能自动切换。

             Gevent:

               Gevent 是一个第三方库,可以轻松通过gevent实现并发同步或异步编程。在gevent中用到的主要模式是Greenlet, 是以C扩展模块形式接入Python的轻量级协程。 Greenlet全部运行在主程序操作系统进程的内部,但它们被协作式地调度。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 一文读懂Python多线程 1、线程和进程 计算机的核心是CPU,它承担了所有的计算任务。它就像一座工厂,时刻在运...
    星丶雲阅读 1,480评论 0 4
  • 她就是他的影子 他想逃离 但是开始练习和自己的影子做朋友 眼泪打湿了逃离的初衷 才明白年少轻狂的自己 有新的注释
    初澜阅读 228评论 0 0
  • 今日看到一句话:假如你胆敢不珍惜自己的注意力,你的注意力就会被无情的被收割,然后被高价卖出去 是的前几天我就发了一...
    逄格亮阅读 276评论 0 0
  • 不知等是我的意志力太差,还是我遵循中国人的随大流素质,本来自己的注意力就很分散,还是那么爱占小便宜。 自己从逻辑思...
    A把时间当做朋友阅读 262评论 1 3
  • 今天周四,又是和好姐妹晚上相约的日子。每逢这天,我总是要和先生请假的。为了减轻自己不能陪先生一起做饭的愧疚感,...
    我们的日常阅读 364评论 0 2