第四模块:网络编程进阶&数据库开发 练习

练习题

基于queue模块实现线程池

import threading
from multiprocessing import Queue

class A(threading.Thread):
   def __init__(self,queue):
      threading.Thread.__init__(self)
      self.data = queue
   def run(self):
      for i in range(100):
         self.data.put(i)

class B(threading.Thread):
   def __init__(self,queue):
      threading.Thread.__init__(self)
      self.data = queue
   def run(self):
      while True:
         try:
            val = self.data.get(True,10)

            print(val)
         except Exception as e:
            print( "Get data timeout!")
            break

def main():
   q = Queue()
   a = A(q)
   b = B(q)
   a.start()
   b.start()
   a.join()
   b.join()

if __name__ == "__main__":
   main()
   print ("test program end.")

 

1、基于多线程实现并发的套接字通信

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# _*_ coding: utf-8 _*_
from socket import *
 
ip_port = ('127.0.0.1',9999)
client = socket(AF_INET,SOCK_STREAM)
client.connect(ip_port)
 
while True:
    cmd = input(">>>").strip()
    if not cmd:
        continue
    client.send(cmd.encode('utf-8'))
    data = client.recv(1024)
    print(data.decode('utf-8'))
client.close()

  

服务端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import multiprocessing
import threading
import socket
 
ip_port = ('127.0.0.1',9999)
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.bind(ip_port)
s.listen(5)
 
def action(conn):
    while True:
        data = conn.recv(1024)
        print(data)
        conn.send(data.upper())
if __name__ == '__main__':
    while True:
        conn,addr = s.accept()
 
        p = threading.Thread(target=action,args=(conn,))
        p.start()

  

2、编写一个简单的文本处理工具,具备三个任务,一个接收用户输入,一个将用户输入的内容格式化成大写,一个将格式化后的结果存入文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# _*_ coding: utf-8 _*_
# 练习二:三个任务,一个接收用户输入,一个将用户输入的内容格式
# 化成大写,一个将格式化后的结果存入文件
 
from threading import Thread
 
msg_l = []
format_l = []
def talk():
    while True:
        msg = input(">>>").strip()
        if not msg:
            break
        msg_l.append(msg)
 
def format_msg():
    while True:
        if msg_l:
            res = msg_l.pop()
            format_l.append(res.upper())
 
def save():
    while True:
        if format_l:
            with open('db.txt','a',encoding='utf-8') as f:
                res = format_l.pop()
                f.write('%s\n'%res)
 
if __name__ == '__main__':
    t1 = Thread(target=talk)
    t2 = Thread(target=format_msg)
    t3 = Thread(target=save)
 
    t1.start()
    t2.start()
    t3.start()

python并发编程课后练习

 

1、简述计算机操作系统中的“中断”的作用?

中断是指在计算机执行期间,系统内发生任何非寻常的或非预期的急需处理事件,使得CPU暂时中断当前正在执行的程序而转去执行相应的时间处理程序。
待处理完毕后又返回原来被中断处继续执行或调度新的进程执行的过程。 当遇到IO操作时或一个进程运行时间过长或被更高的优先级的进程替代时出现中断。有利于合理利用有限的系统资源,提高程序运行效率。

2、简述计算机内存中的“内核态”和“用户态”;

复制代码
处于用户态的程序只能访问用户空间,而处于内核态的程序可以访问用户空间和内核空间。
当一个任务(进程)执行系统调用而陷入内核代码中执行时,我们就称进程处于内核运行态(或简称为内核态)。此时处理器处于特权级最高的(0级)内核代码中执行。当进程处于内核态时,执行的内核代码会使用当前进程的内核栈。每个进程都有自己的内核栈。
当进程在执行用户自己的代码时,则称其处于用户运行态(用户态)。此时处理器在特权级最低的(3级)用户代码中运行。
用户态切换到内核态的3种方式:
1、系统调用:这是用户态进程主动要求切换到内核态的一种方式
2、异常
3、外围设备的中断
复制代码

3、进程间通信方式有哪些?

进程间通信(IPC)方式主要包括以下几种:
    (1)管道,分为有名管道和无名管道,是一种半双工的通信方式。
    (2)信号量,信号量是一个计数器,可以用来控制多个线程对共享资源的访问。
     (3)信号,信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
    (4)消息队列,息队列是消息的链表,存放在内核中并由消息队列标识符标识。
    (5)共享内存,共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问.共享内存是最快的IPC(进程间通信)方式。
    (6)套接字:可用于不同及其间的进程通信。
 

4、简述你对管道、队列的理解;

复制代码
  管道:通常指无名管道
它是半双工的(即数据只能在一个方向上流动),具有固定的读端和写端。
它只能用于具有亲缘关系的进程之间的通信(也是父子进程或者兄弟进程之间)。
它可以看成是一种特殊的文件,对于它的读写也可以使用普通的read、write 等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。
  队列:是消息的链接表。一个消息队列由一个标识符(即队列ID)来标识。
消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级。
消息队列独立于发送与接收进程。进程终止时,消息队列及其内容并不会被删除。
消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取。

  队列和管道都是将数据存放于内存中,而队列是基于“管道+锁”实现的。
复制代码

5、请列举你知道的进程间通信方式;

复制代码
1.管道(无名管道):速度慢,容量有限,只有父子进程能通讯。它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。它是半双工的(即数据只能在一个方向上流动),具有固定的读端和写端。  
2.FIFO(命名管道):任何进程间都能通讯,但速度慢,它是一种文件类型。    
3.消息队列:是消息的链接表,存放在内核中。消息队列独立于发送与接收进程。进程终止时,消息队列及其内容并不会被删除,所以要注意第一次读的时候,要考虑上一次没有读完数据的问题。容量受到系统限制。消息队列可以实现消息的随机查询  
4.信号量:信号量用于实现进程间的互斥与同步,而不是用于存储进程间通信数据。   
5.共享内存区:能够很容易控制容量,速度快,但要保持同步,比如一个进程在写的时候,另一个进程要注意读写的问题,相当于线程中的线程安全,当然,共享内存区同样可以用作线程间通讯,不过没这个必要,线程间本来就已经共享了同一进程内的一块内存
信号量+共享内存通常结合在一起使用,信号量用来同步对共享内存的访问。
复制代码

6、什么是同步I/O,什么是异步I/O?

 
同步IO:当程序发生IO操作时,会将进程阻塞,直到IO操作完成才能继续执行后面的代码。

 同步I/O,用户进程需要主动读写数据。

异步I/O,不需要主动读写数据,只需要读写数据完成的通知。异步IO:进程不会发生阻塞。

总结:
阻塞/非阻塞是内核级的,同步/异步是进程级的,同步非阻塞和异步阻塞一般不会采用,异步非阻塞一般效率比较高,他能够把cpu和io并行处理,当然,如果没有cpu和io并行的这种情况,那就用同步阻塞比较好,这样编程比较简单,且不用多次在内核空间和用户空间进行切换 

7、请问multiprocessing模块中的Value、Array类的作用是什么?举例说明它们的使用场景

复制代码
使用Value或者Array把数据存储在一个共享的内存表中,用于实现进程间共享状态。
Value( typecode, arg1, … argN, lock ) 
在共享内容中常见ctypes对象。typecode要么是包含array模块使用的相同类型代码(如’i’,’d’等)的字符串,要么是来自ctypes模块的类型对象(如ctypes.c_int、ctypes.c_double等)。
所有额外的位置参数arg1, arg2 ….. argN将传递给指定类型的构造函数。lock是只能使用关键字调用的参数,如果把它置为True(默认值),将创建一个新的锁定来包含对值的访问。
如果传入一个现有锁定,比如Lock或RLock实例,该锁定将用于进行同步。如果v是Value创建的共享值的实例,便可使用v.value访问底层的值。例如,读取v.value将获取值,而赋值v.value将修改值。

 RawValue( typecode, arg1, … ,argN) 
同Value对象,但不存在锁定。

 Array( typecode, initializer, lock ) 
在共享内存中创建ctypes数组。typecode描述了数组的内容,意义与Value()函数中的相同。initializer要么是设置数组初始大小的整数,要么是项目序列,其值和大小用于初始化数组。lock是只能使用关键字调用的参数,意义与Value()函数中相同。
如果a是Array创建的共享数组的实例,便可使用标准的python索引、切片和迭代操作访问它的内容,其中每种操作均由锁定进行同步。对于字节字符串,a还具有a.value属性,可以吧整个数组当做一个字符串进行访问。

 RawArray(typecode, initializer ) 
同Array对象,但不存在锁定。当所编写的程序必须一次性操作大量的数组项时,如果同时使用这种数据类型和用于同步的单独锁定(如果需要的话),性能将得到极大的提升。
复制代码
# 进程间共享状态,当然尽最大可能防止使用共享状态,但最终有可能会使用到.
# 1-共享内存
# 可以通过使用Value或者Array把数据存储在一个共享的内存表中
from multiprocessing import Process,Value,Array
import time


def f(n, a, name):
    time.sleep(1)
    n.value = name * name
    for i in range(len(a)):
        a[i] = -i


process_list = []
if __name__ == '__main__':
    num = Value('d',0.0)  # d表示一个双精浮点类型
    arr = Array('i',range(10))  # i表示一个带符号的整型
    for i in range(10):
        p = Process(target=f,args=(num, arr, i))
        p.start()
        process_list.append(p)
    for j in process_list:
        j.join()
    print(num.value)
    print(arr[:])
# 更加灵活的共享内存可以使用multiprocessing.sharectypes模块
View Code

https://blog.csdn.net/winterto1990/article/details/48106505

8、请问multiprocessing模块中的Manager类的作用是什么?与Value和Array类相比,Manager的优缺点是什么?

复制代码
使用Value或者Array把数据存储在一个共享的内存表中。
Manager()返回一个manager类型,控制一个server process,可以允许其它进程通过代理复制一些python objects。
支持:list,dict,Namespace,Lock,Semaphore,BoundedSemaphore,Condition,Event,Queue,value,Array ;
Manager 提供了多进程环境中共享数据的使用的方案, 共享数据可以支持跨进程, 甚至跨机器访问, 以网络通信的方式实现(eg: unix socket, tpc),一个 manager 实例控制一个 server 进程(manager 自己创建的后台进程,对用户透明), 该 server 负责管理共享数据. 其他进程则是用户业务创建的,它们可以通过代理方式来访问 manager 自带 server 的共享数据. 代理可以理解为一个网络通道, 业务进程 和 manager server 进程通信交互数据, 由于server 单线程, 对业务进程通信请求FIFO的方式来处理数据, 因此保证了操作行为的可预期性(当然如果业务进程希望锁定数据, 可以通过代理的 Lock 锁机制来实现).
manger的的优点是可以在poor进程池中使用,缺点是windows下环境下性能比较差,因为windows平台需要把Manager.list放在if name='main'下,而在实例化子进程时,必须把Manager对象传递给子进程,否则lists无法被共享,而这个过程会消耗巨大资源,因此性能很差。
复制代码

https://www.cnblogs.com/shengyang17/p/8987075.html

9、写一个程序,包含十个线程,子线程必须等待主线程sleep 10秒钟之后才执行,并打印当前时间;

复制代码
import time
from threading import Thread, currentThread


def task():
    print("current time:", time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))


if __name__ == "__main__":
    t_l = []
    for i in range(10):  # 开启10个线程
        t = Thread(target=task)
        t_l.append(t)
    for t in t_l:
        time.sleep(10)
        t.start()
复制代码
View Code
from threading import Thread,currentThread,Event
import time

event = Event()

def task():
event.wait()
print("name:%s time:%s"%(currentThread().getName(),time.strftime('%Y-%m-%d %H:%M:%S',time.localtime())))

if __name__ == '__main__':
for i in range(10):
t = Thread(target=task)
t.start()

time.sleep(10)
event.set()

10、写一个程序,包含十个线程,同时只能有五个子线程并行执行;

复制代码
import time
import random
from threading import Thread, currentThread, Semaphore


def task():
    sm.acquire()
    print("%s is running" % currentThread().getName())
    time.sleep(random.randint(1, 3))
    sm.release()


if __name__ == "__main__":
    sm = Semaphore(5)
    for i in range(10):
        t = Thread(target=task, name="线程%s" % i)
        t.start()
复制代码
View Code

11、写一个程序,要求用户输入用户名和密码,要求密码长度不少于6个字符,且必须以字母开头,如果密码合法,则将该密码使用md5算法加密后的十六进制概要值存入名为password.txt的文件,超过三次不合法则退出程序;

import re, hashlib,json
def func():
    count =0
    while count <3:
        username = input('username:').strip()
        password= input("password:").strip()

        if len(password)>=6 and re.search("\A[a-zA-Z]",password):
            obj = {'username':username,'password':hashlib.md5(password.encode('utf-8')).hexdigest()}
            json.dump(obj,open('password.txt','a',encoding='utf-8'))
            print('注册成功')
            break
        else:
            print('密码长度不少于6个字符,且必须以字母开头')
            count += 1
if __name__ == '__main__':
    func()

  

 

 

复制代码
class Client:
    """客户端"""
    def __init__(self, ip, port):
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.sock.connect((ip, port))

    def handle_msg(self):
        """发送、接收消息"""
        while True:
            msg = input(">>>").strip()
            if not msg:
                break
            self.sock.send(msg.encode())
            data = self.sock.recv(1024)
            print("from server:", data.decode())


if __name__ == "__main__":
    client = Client("127.0.0.1", 8080)
    print("server connecting.....")
    client.handle_msg()
复制代码

11、写一个程序,要求用户输入用户名和密码,要求密码长度不少于6个字符,且必须以字母开头,如果密码合法,则将该密码使用md5算法加密后的十六进制概要值存入名为password.txt的文件,超过三次不合法则退出程序;

 

  

import re, hashlib, json
def func():
    count = 0
    while count < 3:
        usename = input(‘usename:‘)
        password = input(‘password:‘)
        if len(password) <6 or not re.search(‘\A([a-z]|[A-Z])‘, password):
            count += 1
        else:
            obj = {‘usename‘:usename, ‘password‘:hashlib.md5(password.encode(‘utf-8‘)).hexdigest()}
            json.dump(obj, open(‘password.txt‘, ‘a‘, encoding=‘utf-8‘))
            break
if __name__ == ‘__main__‘:
    func()

12、写一个程序,使用socketserver模块,实现一个支持同时处理多个客户端请求的服务器,要求每次启动一个新线程处理客户端请求;

 

import socketserver
class Handler(socketserver.BaseRequestHandler):
    def handle(self):
        print(‘connection:‘, self.client_address)
        while True:
            try:
                data = self.request.recv(1024)
                if not data:break
                print(‘client data:‘, data.decode())
                self.request.send(data.upper())
            except Exception as e:
                print(e)
                break
if __name__ == ‘__main__‘:
    server = socketserver.ThreadingTCPServer((‘127.0.0.1‘, 8080), Handler)
    server.serve_forever()
##客户端
import socket
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect((‘127.0.0.1‘, 8080))
while True:
    msg = input(‘>>>:‘)
    if not msg:break
    client.send(msg.encode(‘utf-8‘))
    data = client.recv(1024)
    print(data.decode(‘utf-8‘))

 

进程

1、进程间内存是否共享?如何实现通讯?

进程间内存不共享,使用ipc通讯。

1.管道(无名管道):速度慢,容量有限,只有父子进程能通讯。它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。它是
半双工的(即数据只能在一个方向上流动),具有固定的读端和写端。 2.FIFO(命名管道):任何进程间都能通讯,但速度慢,它是一种文件类型。 3.消息队列:是消息的链接表,存放在内核中。消息队列独立于发送与接收进程。进程终止时,消息队列及其内容并不会被删除,
所以要注意第一次读的时候,要考虑上一次没有读完数据的问题。容量受到系统限制。消息队列可以实现消息的随机查询 4.信号量:信号量用于实现进程间的互斥与同步,而不是用于存储进程间通信数据。 5.共享内存区:能够很容易控制容量,速度快,但要保持同步,比如一个进程在写的时候,另一个进程要注意读写的问题,
相当于线程中的线程安全,当然,共享内存区同样可以用作线程间通讯,不过没这个必要,线程间本来就已经共享了同一进程内的一块内存 信号量+共享内存通常结合在一起使用,信号量用来同步对共享内存的访问。

 

3、请画出进程的三状态转换图

运行-阻塞-就绪 

线程

1、GIL锁是怎么回事?

 Global Interpreter Lock,直译为“全局解释锁”

GIL存在原因

CPython在执行多线程的时候并不是线程安全的,所以为了程序的稳定性,加一把全局解释锁,能够确保任何时候都只有一个Python线程执行。

GIL的弊端

  • GIL对计算密集型的程序会产生影响。因为计算密集型的程序,需要占用系统资源。GIL的存在,相当于始终在进行单线程运算,这样自然就慢了。

  • IO密集型影响不大的原因在于,IO,input/output,这两个词就表明程序的瓶颈在于输入所耗费的时间,线程大部分时间在等待,所以它们是多个一起等(多线程)还是单个等(单线程)无所谓的。

2、在python中是否线程安全?

安全的,有全局解释锁GIL,互斥锁,递归锁。信号量,队列。都可以用来提供线程安全。

3、什么叫死锁?

所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程,如下就是死锁

 

4、logging模块是否是线程安全的?

 

5、threading.local的作用?

为每个线程创建一个独立的空间,使得线程对自己的空间中的数据进行操作(数据隔离)。

6、程序从flag a执行到falg b的时间大致是多少秒?

import threading

import time

def _wait():

time.sleep(60)

# flag a

t = threading.Thread(target=_wait,daemon = False)

t.start()

# flag b

7、程序从flag a执行到falg b的时间大致是多少秒?

import threading

import time

def _wait():

time.sleep(60)

# flag a

t = threading.Thread(target=_wait,daemon = True)

t.start()

# flag b

8、程序从flag a执行到falg b的时间大致是多少秒?

import threading

import time

def _wait():

time.sleep(60)

# flag a

t = threading.Thread(target=_wait,daemon = True)

t.start()

t.join()

# flag b

 

9、读程序,请确认执行到最后number是否一定为0

import threading

loop = int(1E7)

def _add(loop:int = 1):

global number

for _ in range(loop):

number += 1

def _sub(loop:int = 1):

global number

for _ in range(loop):

number -= 1

number = 0

ta = threading.Thread(target=_add,args=(loop,))

ts = threading.Thread(target=_sub,args=(loop,))

ta.start()

ts.start()

ta.join()

ts.join()

 

10、读程序,请确认执行到最后number是否一定为0

import threading

loop = int(1E7)

def _add(loop:int = 1):

global number

for _ in range(loop):

number += 1

def _sub(loop:int = 1):

global number

for _ in range(loop):

number -= 1

number = 0

ta = threading.Thread(target=_add,args=(loop,))

ts = threading.Thread(target=_sub,args=(loop,))

ta.start()

ta.join()

ts.start()

ts.join()

 

11、读程序,请确认执行到最后number的长度是否一定为1

import threading

loop = int(1E7)

def _add(loop:int = 1):

global numbers

for _ in range(loop):

numbers.append(0)

def _sub(loop:int = 1):

global number

while not numbers:

time.sleep(1E-8)

numbers.pop()

number = [0]

ta = threading.Thread(target=_add,args=(loop,))

ts = threading.Thread(target=_sub,args=(loop,))

ta.start()

ta.join()

ts.start()

ts.join()

 

12、读程序,请确认执行到最后number的长度是否一定为1

import threading

loop = int(1E7)

def _add(loop:int = 1):

global numbers

for _ in range(loop):

numbers.append(0)

def _sub(loop:int = 1):

global number

while not numbers:

time.sleep(1E-8)

numbers.pop()

number = [0]

ta = threading.Thread(target=_add,args=(loop,))

ts = threading.Thread(target=_sub,args=(loop,))

ta.start()

ts.start()

ta.join()

ts.join()

 

协程

1、什么是协程?常用的协程模块有哪些?

协程:是单线程下的并发,又称微线程,纤程。英文名Coroutine。一句话说明什么是线程:协程是一种用户态的轻量级线程,即协程是由用户程序自己控制调度的。

  1. 必须在只有一个单线程里实现并发
  2. 修改共享数据不需加锁
  3. 用户程序里自己保存多个控制流的上下文栈
  4. 附加:一个协程遇到IO操作自动切换到其它协程(如何实现检测IO,yield、greenlet都无法实现,就用到了gevent模块(select机制)

常用的协程模块有gevent,greenlet模块

2、协程中的join是用来做什么用的?它是如何发挥作用的?

1. python的线程属于内核级别的,即由操作系统控制调度(如单线程遇到io或执行时间过长就会被迫交出cpu执行权限,切换其他线程运行)
2. 单线程内开启协程,一旦遇到io,就会从应用程序级别(而非操作系统)控制切换,以此来提升效率(!!!非io操作的切换与效率无关)

优点如下:

1. 协程的切换开销更小,属于程序级别的切换,操作系统完全感知不到,因而更加轻量级
2. 单线程内就可以实现并发的效果,最大限度地利用cpu

缺点如下:

1. 协程的本质是单线程下,无法利用多核,可以是一个程序开启多个进程,每个进程内开启多个线程,每个线程内开启协程
2. 协程指的是单个线程,因而一旦协程出现阻塞,将会阻塞整个线程

总结协程特点:

  1. 必须在只有一个单线程里实现并发
  2. 修改共享数据不需加锁
  3. 用户程序里自己保存多个控制流的上下文栈
  4. 附加:一个协程遇到IO操作自动切换到其它协程(如何实现检测IO,yield、greenlet都无法实现,就用到了gevent模块(select机制))

3、使用协程实现并发的tcp server

import gevent
from gevent import socket,monkey
monkey.patch_all()

def server(port):
    s = socket.socket()
    s.bind(('0.0.0.0', port))
    s.listen(500)
    while True:
        cli, addr = s.accept()

        # socket会创建一个线程链接,这里会交给协程处理
        # 链接后通过gevent启动一个协程
        # 接收一个函数,与链接实例参数
        gevent.spawn(handle_request, cli)

# 所有交互都由handle处理
def handle_request(conn):
    try:
        while True:
            data = conn.recv(1024)
            print("recv:", data)
            conn.send(data)
            if not data:
                # 如果没有数据就关闭Client端
                conn.shutdown(socket.SHUT_WR)
    # 如果出现异常就打印异常
    except Exception as  ex:
        print(ex)
    # 最后中断实例的conn
    finally:
        conn.close()
if __name__ == '__main__':
    server(8001)

server.close()
import socket

# 链接服务端
HOST = 'localhost'    # The remote host
PORT = 8001           # The same port as used by the server
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))

# 数据请求
while True:
    msg = bytes(input(">>:"),encoding="utf8")
    s.sendall(msg)
    data = s.recv(1024)
    #print(data)

    # repr格式化输出
    print('Received', repr(data))
s.close()

 

 

4、在一个列表中有多个url,请使用协程访问所有url,将对应的网页内容写入文件保存

 

综合

1、进程和线程的区别

进程:是并发执行的程序在执行过程中分配和管理资源的基本单位,是一个动态概念,竞争计算机系统资源的基本单位。

线程:是进程的一个执行单元,是进程内科调度实体。比进程更小的独立运行的基本单位。线程也被称为轻量级进程。

一个程序至少一个进程,一个进程至少一个线程。

进程有自己的独立地址空间,每启动一个进程,系统就会为它分配地址空间,建立数据表来维护代码段、堆栈段和数据段,

这种操作非常昂贵。而线程是共享进程中的数据的,使用相同的地址空间,因此CPU切换一个线程的花费远比进程要小很

多,同时创建一个线程的开销也比进程要小很多。

2、进程池、线程池的优势和特点 

服务的开启的进程数或线程数都会随着并发的客户端数目地增多而增多,这会对服务端主机带来巨大的压力,甚至于不堪重负而瘫痪,于是我们必须对服务端开启的进程数或线程数加以控制,让机器在一个自己可以承受的范围内运行,这就是进程池或线程池的用途,例如进程池,就是用来存放进程的池子,本质还是基于多进程,只不过是对开启进程的数目加上了限制,让机器可以健康的运行.

3、线程和协程的异同?

协程多与线程进行比较

 一个线程可以多个协程,一个进程也可以单独拥有多个协程,这样python中则能使用多核CPU。

线程进程都是同步机制,而协程则是异步

协程能保留上一次调用时的状态,每次过程重入时,就相当于进入上一次调用的状态

 

4、请简述一下互斥锁和递归锁的异同?

二者的区别是:递归锁可以连续acquire多次,而互斥锁只能acquire一次

 

5、请列举一个python中数据安全的数据类型?

多线程,多进程时操作修改共享数据加锁,才能保证数据安全

6、Python中如何使用线程池和进程池

 

7、简述 进程、线程、协程的区别 以及应用场景?

1.线程和进程:
线程是属于进程的,线程运行在进程空间内,同一进程所产生的线程共享同一内存空间,当进程退出时该进程所产生的线程都会被强制退出并清除。线程可与属于同一进程的其它线程共享进程所拥有的全部资源,但是其本身基本上不拥有系统资源,只拥有一点在运行中必不可少的信息(如程序计数器、一组寄存器和栈)。

2.线程、进程与协程:
线程和进程的操作是由程序触发系统接口,最后的执行者是系统;协程的操作则是程序员
协程存在的意义:对于多线程应用,CPU通过切片的方式来切换线程间的执行,线程切换时需要耗时(保持状态,下次继续)。协程,则只使用一个线程,在一个线程中规定某个代码块执行顺序。
协程的适用场景: 当程序中存在大量不需要CPU的操作时(IO),适用于协程;

 

8、什么是IO多路复用及其作用

IO multiplexing这个词可能有点陌生,但是如果我说select/epoll,大概就都能明白了。有些地方也称这种IO方式为事件驱动IO

(event driven IO)。我们都知道,select/epoll的好处就在于单个process就可以同时处理多个网络连接的IO。它的基本原理就是select/epoll这个function会不断的轮询所负责的所有socket,当某个socket有数据到达了,就通知用户进程。

强调:

1. 如果处理的连接数不是很高的话,使用select/epoll的web server不一定比使用multi-threading + blocking IO的web server性能更好,可能延迟还更大。select/epoll的优势并不是对于单个连接能处理得更快,而是在于能处理更多的连接。

2. 在多路复用模型中,对于每一个socket,一般都设置成为non-blocking,但是,如上图所示,整个用户的process其实是一直被block的。只不过process是被select这个函数block,而不是被socket IO给block。

结论: select的优势在于可以处理多个连接,不适用于单个连接

 

9、select、poll、epoll 模型的区别?

总结:

(1)select,poll实现需要自己不断轮询所有fd集合,直到设备就绪,期间可能要睡眠和唤醒多次交替。而epoll其实也需要调用 epoll_wait
不断轮询就绪链表,期间也可能多次睡眠和唤醒交替,但是它是设备就绪时,调用回调函数,把就绪fd放入就绪链表中,并唤醒在 epoll_wait中
进入睡眠的进程。虽然都要睡眠和交替,但是select和poll在“醒着”的时候要遍历整个fd集合,而epoll在“醒着”的 时候只要判断一下就绪链表
是否为空就行了,这节省了大量的CPU时间,这就是回调机制带来的性能提升。

(2)select,poll每次调用都要把fd集合从用户态往内核态拷贝一次,并且要把current往设备等待队列中挂一次,而epoll只要 一次拷贝,
而且把current往等待队列上挂也只挂一次(在epoll_wait的开始,注意这里的等待队列并不是设备等待队列,只是一个epoll内
部定义的等待队列),这也能节省不少的开销。

12、请谈谈对异步非阻塞的了解?

用户进程发起read操作之后,立刻就可以开始去做其它的事。而另一方面,从kernel的角度,当它受到一个asynchronous read之后,首先它会立刻返回,所以不会对用户进程产生任何block。然后,kernel会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都完成之后,kernel会给用户进程发送一个signal,告诉它read操作完成了。

13、简述信号量的实现原理

信号量也是一把锁,可以指定信号量为5,对比互斥锁同一时间只能有一个任务抢到锁去执行,信号量同一时间可以有5个任务拿到锁去执行,如果说互斥锁是合租房屋的人去抢一个厕所,那么信号量就相当于一群路人争抢公共厕所,公共厕所有多个坑位,这意味着同一时间可以有多个人上公共厕所,但公共厕所容纳的人数是一定的,这便是信号量的大小

14、程序中的阻塞有哪些?给程序带来了哪些影响?

默认情况下所有的socket都是blocking,一个典型的读操作流程大概是这样

所以,blocking IO的特点就是在IO执行的两个阶段(等待数据和拷贝数据两个阶段)都被block了。 send数据IO时间短.

所谓阻塞型接口是指系统调用(一般是IO接口)不返回调用结果并让当前线程一直阻塞
只有当该系统调用获得结果或者超时出错时才返回。

对应上例中的所面临的可能同时出现的上千甚至上万次的客户端请求,“线程池”或“连接池”或许可以缓解部分压力,但是不能解决所有问题。
总之,多线程模型可以方便高效的解决小规模的服务请求,但面对大规模的服务请求,多线程模型也会遇到瓶颈,可以用非阻塞接口来尝试解决这个问题。
selectors模块

15、请分别用多进程、多线程、协程实现生产者消费者模型?

# 多线程
from threading import Thread,currentThread
from multiprocessing import Queue
import os

q = Queue()
def producer(q):
    for i in range(3):
        q.put('name:%s id:%s'%(currentThread().getName(),os.getpid()))


def consumer(q):
    while True:
        res = q.get()
        if not res:break
        print(res)

if __name__ == '__main__':
    t1 = Thread(target=producer,args=(q,))
    t2 = Thread(target=producer, args=(q,))
    t3 = Thread(target=producer, args=(q,))

    t4 = Thread(target=consumer, args=(q,))
    t5 = Thread(target=consumer, args=(q,))

    t1.start()
    t2.start()
    t3.start()
    t4.start()
    t5.start()

    t1.join()
    t2.join()
    t3.join()
    q.put(None)
    q.put(None)
    print('')

 

 

#多进程实现
from multiprocessing import Process,current_process,Queue
from threading import Thread,currentThread
import gevent,os

q = Queue()

def producer(q):
    for i in range(3):
        q.put('%s is running %s'%(os.getpid(),i))

def consumer(q):
    while True:
        res = q.get()
        if not res:break
        print(res)

if __name__ == '__main__':
    p1 = Process(target=producer,args=(q,))
    p2 = Process(target=producer,args=(q,))
    p3 = Process(target= producer,args=(q,))

    p4 = Process(target = consumer,args=(q,))
    p5 = Process(target=consumer, args=(q,))

    p1.start()
    p2.start()
    p3.start()
    p4.start()
    p5.start()

    p1.join()
    p2.join()
    p3.join()
    q.put(None)
    q.put(None)
    print('')

 

#1. 操作文件夹
        增:create database db1 charset utf8;
        查:show databases;
        改:alter database db1 charset latin1;
        删除: drop database db1;


#2. 操作文件
    先切换到文件夹下:use db1
        增:create table t1(id int,name char);
        查:show tables
        改:alter table t1 modify name char(3);
              alter table t1 change name name1 char(2);
        删:drop table t1;


#3. 操作文件中的内容/记录
        增:insert into t1 values(1,'egon1'),(2,'egon2'),(3,'egon3');
        查:select * from t1;
        改:update t1 set name='sb' where id=2;
        删:delete from t1 where id=1;            

查看数据库
show databases;
show create database db1;
select database();
选择数据库
USE 数据库名
删除数据库
DROP DATABASE 数据库名;
修改数据库
alter database db1 charset utf8;
MariaDB [(none)]> show engines\G  #查看所有支持的存储引擎

MariaDB [(none)]> show variables like 'storage_engine%'; #查看正在使用的存储引擎

一 单表查询的语法

SELECT 字段1,字段2... FROM 表名
                  WHERE 条件
                  GROUP BY field
                  HAVING 筛选
                  ORDER BY field
                  LIMIT 限制条数

二 关键字的执行优先级(重点)

重点中的重点:关键字的执行优先级
from
where
group by
having
select
distinct
order by
limit

创建视图

#语法:CREATE VIEW 视图名称 AS  SQL语句
create view teacher_view as select tid from teacher where tname='李平老师';

删除视图

语法:DROP VIEW 视图名称

DROP VIEW teacher_view
 

Pymysql 模块增、删、改:conn.commit()

import pymysql
#链接
conn=pymysql.connect(host='localhost',user='root',password='123',database='egon')
#游标
cursor=conn.cursor()

#执行sql语句
#part1
# sql='insert into userinfo(name,password) values("root","123456");'
# res=cursor.execute(sql) #执行sql语句,返回sql影响成功的行数
# print(res)

#part2
# sql='insert into userinfo(name,password) values(%s,%s);'
# res=cursor.execute(sql,("root","123456")) #执行sql语句,返回sql影响成功的行数
# print(res)

#part3
sql='insert into userinfo(name,password) values(%s,%s);'
res=cursor.executemany(sql,[("root","123456"),("lhf","12356"),("eee","156")]) #执行sql语句,返回sql影响成功的行数
print(res)

conn.commit() #提交后才发现表中插入记录成功
cursor.close()
conn.close()

查:fetchone,fetchmany,fetchall

import pymysql
#链接
conn=pymysql.connect(host='localhost',user='root',password='123',database='egon')
#游标
cursor=conn.cursor()

#执行sql语句
sql='select * from userinfo;'
rows=cursor.execute(sql) #执行sql语句,返回sql影响成功的行数rows,将结果放入一个集合,等待被查询

# cursor.scroll(3,mode='absolute') # 相对绝对位置移动
# cursor.scroll(3,mode='relative') # 相对当前位置移动
res1=cursor.fetchone()
res2=cursor.fetchone()
res3=cursor.fetchone()
res4=cursor.fetchmany(2)
res5=cursor.fetchall()
print(res1)
print(res2)
print(res3)
print(res4)
print(res5)
print('%s rows in set (0.00 sec)' %rows)



conn.commit() #提交后才发现表中插入记录成功
cursor.close()
conn.close()

'''
(1, 'root', '123456')
(2, 'root', '123456')
(3, 'root', '123456')
((4, 'root', '123456'), (5, 'root', '123456'))
((6, 'root', '123456'), (7, 'lhf', '12356'), (8, 'eee', '156'))
rows in set (0.00 sec)
'''

获取插入的最后一条数据的自增ID

import pymysql
conn=pymysql.connect(host='localhost',user='root',password='123',database='egon')
cursor=conn.cursor()

sql='insert into userinfo(name,password) values("xxx","123");'
rows=cursor.execute(sql)
print(cursor.lastrowid) #在插入语句后查看

conn.commit()

cursor.close()
conn.close()
 
 




posted @ 2018-10-23 11:46  andyzhang-  阅读(338)  评论(0编辑  收藏  举报