并发编程小结

1、到底什么是线程?什么是进程?

Python本身没有这玩意,Python中调用的操做系统的线程和进程。git

2、Python多线程状况下:

计算密集型操做:效率低,Python内置的一个全局解释器锁,锁的做用就是保证同一时刻一个进程中只有一个线程能够被cpu调度,多线程没法利用多核优点,能够经过多进程方式解决,可是比较浪费资源。
IO操做:效率高程序员

3、Python多进程的状况下:

计算密集型操做:效率高(浪费资源),不得已而为之。
IO操做:效率高(浪费资源)github

4、为何有这把GIL锁?

Python语言的创始人在开发这门语言时,目的快速把语言开发出来,若是加上GIL锁(C语言加锁),切换时按照100条字节指令来进行线程间的切换。web

5、Python中线程和进程(GIL锁)

GIL锁,全局解释器锁。用于限制一个进程中同一时刻只有一个线程被cpu调度。
扩展:默认GIL锁在执行100个cpu指令(过时时间)。
查看GIL切换的指令个数多线程

import sys
v1 = sys。getcheckinterval()
print(v1)

6、为何要建立线程?

因为线程是cpu工做的最小单元,建立线程能够利用多核优点实现并行操做(Java/C#)。
注意:线程是为了工做。并发

7、为何要建立进程?

进程和进程之间作数据隔离(Java/C#)。app

注意:进程是为了提供环境让线程工做。框架

8、进程和线程的区别?

进程是资源分配的最小单位,线程是程序执行的最小单位。异步

进程有本身的独立地址空间,每启动一个进程,系统就会为它分配地址空间,创建数据表来维护代码段,堆栈段和数据段,这种操做很是昂贵。而线程是共享进程中的数据的,使用相同的地址空间,所以CPU切换一个线程的花费远比进程小不少,同时建立一个线程的开销比进程要小不少。

线程之间的通讯更方便,同一进程下的线程共享全局变量,静态变量,而进程之间的通讯须要以通讯的方式(IPC)进行。不过如何处理好同步与互斥是编写多线程程序的难点。

可是多进程程序更健壮,多线程程序只要有一个线程死掉,整个进程也死掉了,而一个进程死掉并不会对另一个进程形成影响,由于进程有本身独立的地址空间。

也正是因为GIL锁的缘由:IO密集型操做可使用多线程,计算密集型可使用多进程。

9、线程建立的越多越好吗?

很差。线程之间进行切换时,要作上下文管理。

10、生产者消费者模型解决了什么问题?

不用一直等待的问题。

11、Lock和RLock的区别?

RLock能够屡次加锁。

12、进程和线程以及协程的区别?

进程是cpu资源分配的最小单元,一个进程中能够有多个线程。

线程是cpu计算的最小单元。

对于Python来讲他的进程和线程和其余语言有差别,是有GIL锁。

GIL锁保证一个进程中同一时刻只有一个线程被cpu调度。

注意:IO密集型操做可使用多线程,计算密集型可使用多进程。

协程,是由程序员创造出来的一个不是真实存在的东西。

协程,是微线程,对一个线程进程分片,使得线程在代码块之间进行来回切换执行,而不是在原来逐行执行。

十3、IO多路复用做用?

检测多个socket是否已经发生变化(是否已经链接成功/是否已经获取数据)(可读/可写)IO多路复用做用?

检测多个socket是否发生变化。

操做系统检测socket是否发生变化,有三种模式:

  • select:最多1024个socket;循环去检测。
  • poll:不限制监听socket个数;循环去检测(水平触发)。
  • epoll:不限制监听socket个数;回调方式(边缘触发)。

Python模块:

  • select.select
  • select.epoll

十4、socket默认是不是阻塞的?阻塞体如今哪里?

默认是阻塞,填在等待消息和链接

十5、什么是异步非阻塞?

非阻塞,不等待。好比建立socket对某个地址进行connect、获取接收数据recv时默认都会等待(链接成功或接收到数据),才执行后续操做。
若是设置setblocking(False),以上两个过程就再也不等待,可是会报BlockingIOError的错误,只要捕获便可。

异步,通知,执行完成以后自动执行回调函数或自动执行某些操做(通知)。好比作爬虫中向某个地址baidu。com发送请求,当请求执行完成以后自执行回调函数。

十6、什么是同步阻塞?

  • 阻塞:等
  • 同步:按照顺序逐步执行

十7、什么是协程?

协程也能够称为“微线程”,就是开发者控制线程执行流程,控制先执行某段代码而后再切换到另外函执行代码...来回切换。

十8、协程能够提升并发吗?

协程本身自己没法实现并发(甚至性能会下降)。
协程+IO切换性能提升。

十9、提升并发方案:

  • 多进程
  • 多线程
  • 单线程提供并发

二10、单线程提供并发

20.1 基于IO多路复用+socket非阻塞

实现并发请求(一个线程100个请求)

import socket
# 建立socket
client = socket.socket()
# 将原来阻塞的位置变成非阻塞(报错)
client.setblocking(False)

# 百度建立链接: 阻塞
try:
    # 执行了但报错了
    client.connect(('www.baidu.com',80))
except BlockingIOError as e:
    pass

# 检测到已经链接成功

# 问百度我要什么?
client.sendall(b'GET /s?wd=alex HTTP/1.0\r\nhost:www.baidu.com\r\n\r\n')

# 我等着接收百度给个人回复
chunk_list = []
while True:
    # 将原来阻塞的位置变成非阻塞(报错)
    chunk = client.recv(8096) 
    if not chunk:
        break
    chunk_list.append(chunk)

body = b''.join(chunk_list)
print(body.decode('utf-8'))

20.2 协程+IO切换:gevent

from gevent import monkey
# 之后代码中遇到IO都会自动执行greenlet的switch进行切换
monkey.patch_all() 
import requests
import gevent


def get_page1(url):
    ret = requests.get(url)
    print(url,ret.content)

def get_page2(url):
    ret = requests.get(url)
    print(url,ret.content)

def get_page3(url):
    ret = requests.get(url)
    print(url,ret.content)

gevent.joinall([
    gevent.spawn(get_page1, 'https://www.python.org/'), # 协程1
    gevent.spawn(get_page2, 'https://www.yahoo.com/'),  # 协程2
    gevent.spawn(get_page3, 'https://github.com/'),     # 协程3
])

20.3 基于事件循环的异步

执行完某我的物后自动调用我给他的函数,非阻塞

20.3.1 方法一

import socket
import select

# 百度建立链接:非阻塞
client1 = socket.socket()
client1.setblocking(False)
try:
    client1.connect(('www.baidu.com', 80))
except BlockingIOError as e:
    pass

# 搜狗建立链接:非阻塞
client2 = socket.socket()
client2.setblocking(False)
try:
    client2.connect(('www.sogou.com', 80))
except BlockingIOError as e:
    pass

# GitHub建立链接:非阻塞
client3 = socket.socket()
client3.setblocking(False)
try:
    client3.connect(('www.github.com', 80))
except BlockingIOError as e:
    pass

# 建立socket列表:socket_list
socket_list = [client1, client2, client3]
# 建立connect列表:conn_list
conn_list = [client1, client2, client3]

while True:
    rlist, wlist, elist = select.select(socket_list, conn_list, [], 0.005)
    # rlist中表示已近获取数据的socket对象
    # wlist中表示已经链接成功的socket对象
    # elist中表示出现错误的socket对象
    for sk in wlist:
        if sk == client1:
            sk.sendall(b'GET /s?wd=alex HTTP/1.0\r\nhost:www.baidu.com\r\n\r\n')
        elif sk == client2:
            sk.sendall(b'GET /web?query=fdf HTTP/1.0\r\nhost:www.sogou.com\r\n\r\n')
        else:
            sk.sendall(b'GET /s?wd=alex HTTP/1.0\r\nhost:www.oldboyedu.com\r\n\r\n')
        conn_list.remove(sk)
    for sk in rlist:
        chunk_list = []
        while True:
            try:
                chunk = sk.recv(8096)
                if not chunk:
                    break
                chunk_list.append(chunk)
            except BlockingIOError as e:
                break
        body = b''.join(chunk_list)
        # print(body.decode('utf-8'))
        print('------------>', body)
        sk.close()
        socket_list.remove(sk)
    if not socket_list:
        break

20.3.2 方法二

import socket
import select

class Req(object):
    def __init__(self,sk,func):
        self.sock = sk
        self.func = func

    def fileno(self):
        return self.sock.fileno()


class Nb(object):

    def __init__(self):
        self.conn_list = []
        self.socket_list = []

    def add(self,url,func):
        # 建立socket客户端
        client = socket.socket()
        # 非阻塞
        client.setblocking(False)
        try:
            # 建立链接
            client.connect((url, 80))
            # 异常处理
        except BlockingIOError as e:
            pass
        obj = Req(client,func)
        # 链接列表
        self.conn_list.append(obj)
        # socket列表
        self.socket_list.append(obj)

    def run(self):

        while True:
            rlist,wlist,elist = select.select(self.socket_list,self.conn_list,[],0.005)
            # wlist中表示已经链接成功的req对象
            for sk in wlist:
                # 发生变换的req对象
                sk.sock.sendall(b'GET /s?wd=alex HTTP/1.0\r\nhost:www.baidu.com\r\n\r\n')
                self.conn_list.remove(sk)
            for sk in rlist:
                chunk_list = []
                while True:
                    try:
                        chunk = sk.sock.recv(8096)
                        if not chunk:
                            break
                        chunk_list.append(chunk)
                    except BlockingIOError as e:
                        break
                body = b''.join(chunk_list)
                # print(body.decode('utf-8'))
                sk.func(body)
                sk.sock.close()
                self.socket_list.remove(sk)
            if not self.socket_list:
                break


def baidu_repsonse(body):
    print('百度下载结果:',body)

def sogou_repsonse(body):
    print('搜狗下载结果:', body)

def github_repsonse(body):
    print('GITHUB下载结果:', body)


t1 = Nb()
t1.add('www.baidu.com',baidu_repsonse)
t1.add('www.sogou.com',sogou_repsonse)
t1.add('www.github.com',oldboyedu_repsonse)
t1.run()

20.4 基于事件循环的异步非阻塞框架

如Twisted框架,scrapy框架(单线程完成并发)

相关文章
相关标签/搜索