并发编程

练习题: 

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

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

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

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

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

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

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

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

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

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

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

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

 

综合练习:

  基于线程开发一个FTP服务器

  作业需求:

1.在之前开发的FTP基础上,开发支持多并发的功能

2.不能使用SocketServer模块,必须自己实现多线程

3.必须用到队列Queue模块,实现线程池

4.允许配置最大并发数,比如允许只有10个并发用户

 

此处是求知的分割线(一定要先尝试做一下再接着往下看)

--------------------------------------------------------------------------------------------------------------------------------------------

练习题参考答案:

test_1

# 1、当程序遇到io阻塞或程序运行时间过长时CPU会“切”(中断)。
    中断:计算机执行期间,系统内发生任何非寻常的或非预期的急需处理事件,使得CPU暂时中断当前正在执行的程序转而去执行相应的事件处理程序。
    待处理完毕后又返回原来被中断处继续执行或调度新的进程执行的过程。
    它使计算机可以更好更快利用有限的系统资源解决系统响应速度和运行效率的一种控制技术。
    实时响应+系统调用
View Code

test_2

# 2、内核态:运行操作系统的程序,os的数据存放;
        用户态:运行用户程序,用户进程的数据存放。
        用户态的应用程序可以通过三种方式来访问内核态的资源:
            (1)系统调用,(2)库函数,(3)shell脚本
        用户态到内核态的切换:
            (1)系统调用    用户程序主动发起的 软中断 如os.fork() process
            (2)异常     被动的  当CPU正在执行运行在用户态的程序时,突然发生某些预先不可知的异常事件,这个时候就会触发当前用户态执行的进程转向内核态执行相关的异常事件,典型的如缺页异常。
             (3)外围设备的硬中断  被动的  外围设备完成用户的请求操作后,会向CPU发出中断信号,此时,CPU就会暂停执行下一条将要执行的指令,转而去执行中断信号对应的处理程序,如果先前执行的指令是在用户态下,则自然就发生从用户态到内核态的转换。
View Code

test_3

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

 test_4

管道:
管道允许在进程之间按先进先出的方式传送数据,是进程间通信的一种常见方式。
管道是Linux 支持的最初Unix IPC形式之一,具有以下特点:

1) 管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道;

2) 匿名管道只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程);

3) 单独构成一种独立的文件系统:管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,它不属于某种文件系统,而是自立门户,单独构成一种文件系统,并且只存在与内存中。

 
管道分为pipe(无名管道)和fifo(命名管道)两种,除了建立、打开、删除的方式不同外,这两种管道几乎是一样的。他们都是通过内核缓冲区实现数据传输。

pipe用于相关进程之间的通信,例如父进程和子进程,它通过pipe()系统调用来创建并打开,当最后一个使用它的进程关闭对他的引用时,pipe将自动撤销。
FIFO即命名管道,在磁盘上有对应的节点,但没有数据块——换言之,只是拥有一个名字和相应的访问权限,通过mknode()系统调用或者mkfifo()函数来建立的。一旦建立,任何进程都可以通过文件名将其打开和进行读写,而不局限于父子进程,当然前提是进程对FIFO有适当的访问权。当不再被进程使用时,FIFO在内存中释放,但磁盘节点仍然存在。
管道的实质是一个内核缓冲区,进程以先进先出的方式从缓冲区存取数据:管道一端的进程顺序地将进程数据写入缓冲区,另一端的进程则顺序地读取数据,该缓冲区可以看做一个循环队列,读和写的位置都是自动增加的,一个数据只能被读一次,读出以后再缓冲区都不复存在了。当缓冲区读空或者写满时,有一定的规则控制相应的读进程或写进程是否进入等待队列,当空的缓冲区有新数据写入或慢的缓冲区有数据读出时,就唤醒等待队列中的进程继续读写。


消息队列:
消息队列,就是一个消息的链表,是一系列保存在内核中消息的列表。用户进程可以向消息队列添加消息,也可以向消息队列读取消息。

消息队列与管道通信相比,其优势是对每个消息指定特定的消息类型,接收的时候不需要按照队列次序,而是可以根据自定义条件接收特定类型的消息。

可以把消息看做一个记录,具有特定的格式以及特定的优先级。对消息队列有写权限的进程可以向消息队列中按照一定的规则添加新消息,对消息队列有读权限的进程可以从消息队列中读取消息。
进程间通过消息队列通信,主要是:创建或打开消息队列,添加消息,读取消息和控制消息队列。
详情查看:https://www.cnblogs.com/LUO77/p/5816326.html
View Code

 test_5

管道、信号量、消息队列、信号、共享内存、套接字
View Code

 test_6

    同步I/O(synchronous  io):做“IO operation”的时候会将process阻塞,“IO operation”是指真实的IO操作。blocking IO(阻塞IO)、non-blocking IO(非阻塞IO)、IO multiplexing(多路复用IO)都属于同步I/O这一类。
    异步I/O(asynchronous io):当进程发起IO操作之后,就直接返回再也不理睬了,直到kernel发送一个信号,告诉进程说IO完成。在这个过程中,进程完全没有被block.异步io的实现会负责把数据从内核拷贝到用户空间。
View Code

test_7

test_8

test_9

from threading import Thread
import time
def producer(name):
    print('%s is running %s'%(name,i))
    print(time.time())
if __name__ == '__main__':
    print('主线程')
    time.sleep(10)
    for i in range(10):
        p = Thread(target=producer,args=('子线程%s'%i,))
        p.start()
View Code

test_10

from threading import Thread,currentThread
from concurrent.futures import ThreadPoolExecutor
import time
import random
def task():
    print(currentThread().getName())
    time.sleep(random.randint(1,3))

if __name__ == '__main__':
    pool = ThreadPoolExecutor(5)
    for i in range(10):
        pool.submit(task)
View Code

test_11

import re
import hashlib
import json
def save_file(file):
    # 存入文件
    json.dump(file,open('password.txt','a',encoding='utf-8'))

def made_md5(file):
    # md5算法
    return hashlib.md5(file.encode('utf-8')).hexdigest()

def auth():
    # 验证身份
    count = 0
    while count<3:
        user = input('用户名>>:').strip()
        pwd = input('密码>>:').strip()
        if len(pwd)>=6 and (re.match('[A-Za-z]',pwd)):
            print('success')
            pwd_md5 = made_md5(pwd)
            obj = {'username':user,'password':pwd_md5}
            save_file(obj)
            break
        else:
            print('failed')
        count += 1

if __name__ == '__main__':
    auth()
View Code

test_12

# server.py


import socketserver
class Handler(socketserver.BaseRequestHandler): # 必须继承BaseRequestHandler
    def handle(self): # 必须有handle方法
        print('new 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)
if __name__ == '__main__':
    server = socketserver.ThreadingTCPServer(('127.0.0.1',8080),Handler) # 实例化对象,实现多线程的socket
    server.serve_forever()  # 事件监听,并监听handle方法



# client.py

from socket import *
client = socket(family=AF_INET,type=SOCK_STREAM)
client.connect(('127.0.0.1',8080))
while True:
    msg = input('>>>:').strip()
    if not msg:continue
    client.send(msg.encode('utf-8'))
    server_data = client.recv(1024)
    print(server_data.decode('utf-8'))
View Code

 

posted @ 2018-05-26 20:16  暮光微凉  阅读(198)  评论(0编辑  收藏  举报