练习题||并发编程

线程、进程、队列、IO多路模型

操做系统工做原理介绍、线程、进程演化史、特色、区别、互斥锁、信号、事件、join、GIL、进程间通讯、管道、队列。
生产者消息者模型、异步模型、IO多路复用模型、select\poll\epoll 高性能IO模型源码实例解析、高并发FTP server开发
```html

1、问答题

一、简述计算机操做系统中的“中断”的做用?python

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

 


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

操做系统由操做系统的内核(运行于内核态,管理硬件资源)以及 系统调用(运行于用户态,为应用程序员写的应用程序提供系统调用接口)两部分组成; 内核态:cpu能够访问内存的全部数据,包括外围设备,例如硬盘,网卡,cpu也能够将本身从一个程序切换到另外一个程序。 用户态:只能受限的访问内存,且不容许访问外围设备,占用cpu的能力被剥夺,cpu资源能够被其余程序获取。

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

详见:用户态和内核态的区别 https://blog.csdn.net/youngyoungla/article/details/53106671程序员

https://blog.csdn.net/qq_34228570/article/details/72995997web

 

 

三、什么是进程?
进程:正在进行的一个过程或者说一个任务。而负责执行任务则是cpu。算法


四、什么是线程?
线程顾名思义,就是一条流水线工做的过程(流水线的工做须要电源,电源就至关于cpu),而一条流水线必须属于一个车间,
一个车间的工做过程是一个进程,车间负责把资源整合到一块儿,是一个资源单位,而一个车间内至少有一条流水线。
因此,进程只是用来把资源集中到一块儿(进程只是一个资源单位,或者说资源集合),而线程才是cpu上的执行单位。编程


五、简述程序的执行过程;json

1.激活了python的解释器,有一个解释器级别的垃圾回收线程(GIL锁)。 2.一个进程下的多个线程去访问解释器的代码,拿到执行权限,将程序看成参数传递给解释器的代码去执行。 3.保护不一样的数据应该用不一样的锁。 4.python程序是顺序执行的! 5.一段python程序以.py文件运行时,文件属性__name__==__main__;做为模块导入时,文件属性__name__为文件名。

 


六、什么是“系统调用”?windows

全部用户程序都是运行在用户态的,可是有时候程序确实须要作一些内核态的事情,例如从硬盘读取数据,或者从键盘获取输入等, 而惟一能作这些事情的就是操做系统,因此此时程序就须要向操做系统请求以程序的名义来执行这些操做。 这时,就须要一个机制:用户态程序切换到内核态,可是不能控制在内核态中执行的指令。这种机制就叫系统调用。

 

 

七、threading模块event和condition的区别;数组

condition参考:https://blog.csdn.net/a349458532/article/details/51590040
https://blog.csdn.net/u013346751/article/details/78500412
condition: 某些事件触发或达到特定的条件后才处理数据,默认建立了一个lock对象。 con = threading.Condition() con.acquire() con.notify() con.wait() con.release() 

event:其余线程须要经过判断某个线程的状态来肯定本身的下一步操做,就能够用event。 from threading import Event event = Event() event.set(): 设置event的状态值为True,全部阻塞池的线程激活进入就绪状态, 等待操做系统调度; event.is_set():返回event的状态值; event.wait():若是 event.is_set()==False将阻塞线程; event.clear():恢复event的状态值为False。



八、进程间通讯方式有哪些?
管道、信号量、信号、消息队列、共享内存、套接字

 进程间通讯(IPC)   1)管道  管道分为有名管道和无名管道 无名管道是一种半双工的通讯方式,数据只能单向流动,并且只能在具备亲缘关系的进程间使用.进程的亲缘关系通常指的是父子关系。无明管道通常用于两个不一样进程之间的通讯。
当一个进程建立了一个管道,并调用fork建立本身的一个子进程后,父进程关闭读管道端,子进程关闭写管道端,这样提供了两个进程之间数据流动的一种方式。 有名管道也是一种半双工的通讯方式,可是它容许无亲缘关系进程间的通讯。   
2)信号量 信号量是一个计数器,能够用来控制多个线程对共享资源的访问.,它不是用于交换大批数据,而用于多线程之间的同步.它常做为一种锁机制,防止某进程在访问资源时其它进程也访问该资源.
所以,主要做为进程间以及同一个进程内不一样线程之间的同步手段.   
3)信号 信号是一种比较复杂的通讯方式,用于通知接收进程某个事件已经发生.   4)消息队列 消息队列是消息的链表,存放在内核中并由消息队列标识符标识.消息队列克服了信号传递信息少,管道只能承载无格式字节流以及缓冲区大小受限等特色.
消息队列是UNIX下不一样进程之间可实现共享资源的一种机制,UNIX容许不一样进程将格式化的数据流以消息队列形式发送给任意进程.
对消息队列具备操做权限的进程均可以使用msget完成对消息队列的操做控制.经过使用消息类型,进程能够按任何顺序读信息,或为消息安排优先级顺序.   
5)共享内存 共享内存就是映射一段能被其余进程所访问的内存,这段共享内存由一个进程建立,但多个进程均可以访问.共享内存是最快的IPC(进程间通讯)方式,
它是针对其它进程间通讯方式运行效率低而专门设计的.它每每与其余通讯机制,如信号量,配合使用,来实现进程间的同步与通讯.   
6)套接字:可用于不一样及其间的进程通讯

 


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

管道一般指无名管道     1、它是半双工的(即数据只能在一个方向上流动),具备固定的读端和写端     2、它只能用于具备亲缘关系的进程中通讯(也就是父与子进程或者兄弟进程之间)     3、数据不可反复读取了,即读了以后欢喜红区中就没有了   消息队列     1、消息队列是面向记录的,其中的消息具备特定的格式以及特定的优先级     2、消息队列独立于发送与接收进程。进程终止时,消息队列及其内容不会被删除。     3、消息队列能够实现消息随机查询。   mutiprocessing模块为咱们提供的基于消息的IPC通讯机制:队列和管道。   队列和管道都是将数据存放于内存中,而队列又是基于(管道+锁)实现的, 可让咱们从复杂的锁问题中解脱出来,于是队列才是进程间通讯的最佳选择。   咱们应该尽可能避免使用共享数据,尽量使用消息传递和队列,避免处理复杂的同步和锁问题, 并且在进程数目增多时,每每能够得到更好的可展性。 队列 = 管道 +from multiprocessing import Queue,Process queue = Queue() queue.put(url) url = queue.get() from multiprocessing import Pipe,Process pipe = Pipe() pipe.send(url) pipe.recv() 

 

十、请简述你对join、daemon方法的理解,举出它们在生产环境中的使用场景;

join: 等待一个任务执行完毕;能够将并发变成串行。 daemon: 守护进程(守护线程)会等待主进程(主线程)运行完毕后被销毁。 运行完毕: 1.对主进程来讲,运行完毕指的是主进程代码运行完毕。 2.对主线程来讲,运行完毕指的是主线程所在的进程内全部非守护线程通通运行完毕,主线程才算运行完毕。

 

十一、请简述IO多路复用模型的工做原理;

IO多路复用实际上就是用select,poll,epoll监听多个io对象,当io对象有变化(有数据)的时候就通知用户进程。好处就是单个进程能够处理多个socket。 1.当用户进程调用了select,那么整个进程会被block; 2.而同时,kernel会“监视”全部select负责的socket; 3.当任何一个socket中的数据准备好了,select就会返回; 4.这个时候用户进程再调用read操做,将数据从kernel拷贝到用户进程。 总结: 1.I/O 多路复用的特色是经过一种机制一个进程能同时等待多个文件描述符,而这些文件描述符(套接字描述符)其中的任意一个进入读就绪状态,select()函数就能够返回。 2.IO多路复用:须要两个系统调用,system call (select 和 recvfrom),而blocking IO只调用了一个system call (recvfrom)。可是,
  用select的优点在于它能够同时处理多个connection。
3.若是处理的链接数不是很高的话,使用select/epoll的web server不必定比使用多线程 + 阻塞 IO的web server性能更好,可能延迟还更大。 4.select/epoll的优点并非对于单个链接能处理得更快,而是在于能处理更多的链接。

 

 

十二、threading中Lock和RLock的相同点和不一样点;

Lock():互斥锁,只能被acquire一次,可能会发生死锁状况。 RLock():递归锁,能够连续acquire屡次。 RLock = Lock + counter counter:记录了acquire的次数,直到一个线程全部的acquire都被release,其余线程才能得到资源。

 


1三、什么是select,请简述它的工做原理,简述它的优缺点;

python中的select模块专一于I/O多路复用,提供了select poll epoll三个方法;后两个在linux中可用,windows仅支持select。 fd:文件描述符 fd_r_list,fd_w_list,fd_e_list = select.select(rlist,wlist,xlist,[timeout]) 参数:可接受四个参数(前三个必须) rlist:等到准备好阅读 wlist:等到准备写做 xlist:等待“异常状况” 超时:超时时间 返回值:三个列表 select监听fd变化的过程分析: 用户进程建立socket对象,拷贝监听的fd到内核空间,每个fd会对应一张系统文件表,内核空间的fd响应到数据后, 就会发送信号给用户进程数据已到; 用户进程再发送系统调用,好比(accept)将内核空间的数据copy到用户空间,同时做为接受数据端内核空间的数据清除, 这样从新监听时fd再有新的数据又能够响应到了(发送端由于基于TCP协议因此须要收到应答后才会清除)。 该模型的优势: 相比其余模型,使用select() 的事件驱动模型只用单线程(进程)执行,占用资源少,不消耗太多 CPU,同时可以为多客户端提供服务。 若是试图创建一个简单的事件驱动的服务器程序,这个模型有必定的参考价值。 该模型的缺点: 首先select()接口并非实现“事件驱动”的最好选择。由于当须要探测的句柄值较大时,select()接口自己须要消耗大量时间去轮询各个句柄。 不少操做系统提供了更为高效的接口,如linux提供了epoll,BSD提供了kqueue,Solaris提供了/dev/poll,…。 若是须要实现更高效的服务器程序,相似epoll这样的接口更被推荐。遗憾的是不一样的操做系统特供的epoll接口有很大差别, 因此使用相似于epoll的接口实现具备较好跨平台能力的服务器会比较困难。 其次,该模型将事件探测和事件响应夹杂在一块儿,一旦事件响应的执行体庞大,则对整个模型是灾难性的。

 

 

1四、什么是epoll,请简述它的工做原理,简述它的优缺点;

epoll: 性能最好的多路复用I/O就绪通知方法。相比于select,epoll最大的好处在于它不会随着监听fd数目的增加而下降效率。 由于在内核中的select实现中,它是采用轮询来处理的,轮询的fd数目越多,天然耗时越多。 epoll:一样只告知那些就绪的文件描述符,并且当咱们调用epoll_wait()得到就绪文件描述符时,返回的不是实际的描述符,而是一个表明就绪描述符数量的值, 你只须要去epoll指定的一个数组中依次取得相应数量的文件描述符便可,这里也使用了内存映射(mmap)技术,这样便完全省掉了这些文件描述符在系统调用时复制的开销。 另外一个本质的改进在于epoll采用基于事件的就绪通知方式。在select/poll中,进程只有在调用必定的方法后,内核才对全部监视的文件描述符进行扫描, 而epoll事先经过epoll_ctl()来注册一个文件描述符,一旦基于某个文件描述符就绪时,内核会采用相似callback的回调机制,迅速激活这个文件描述符, 当进程调用epoll_wait()时便获得通知。从以上可知,epoll是对select、poll模型的改进,提升了网络编程的性能,普遍应用于大规模并发请求的C/S架构中。 python中的epoll: 只适用于unix/linux操做系统

 


1五、简述select和epoll的区别;

select: 调用select()时   1、上下文切换转换为内核态   2、将fd从用户空间复制到内核空间   3、内核遍历全部fd,查看其对应事件是否发生   4、若是没发生,将进程阻塞,当设备驱动产生中断或者timeout时间后,将进程唤醒,再次进行遍历   5、返回遍历后的fd   6、将fd从内核空间复制到用户空间 select: 缺点 1、当文件描述符过多时,文件描述符在用户空间与内核空间进行copy会很费时   2、当文件描述符过多时,内核对文件描述符的遍历也很浪费时间   3、select最大仅仅支持1024个文件描述符 epoll很好的改进了select:   1、epoll的解决方案在epoll_ctl函数中。每次注册新的事件到epoll句柄中时,会把全部的fd拷贝进内核,而不是在epoll_wait的时候重复拷贝。epoll保证了每一个fd在整个过程当中只会拷贝一次。   2、epoll会在epoll_ctl时把指定的fd遍历一遍(这一遍必不可少)并为每一个fd指定一个回调函数,当设备就绪,唤醒等待队列上的等待者时,就会调用这个回调函数,而这个回调函数会把就绪的fd加入一个就绪链表。 epoll_wait的工做实际上就是在这个就绪链表中查看有没有就绪的fd。   3、epoll对文件描述符没有额外限制。

 


1六、简述多线程和多进程的使用场景;
多进程用于计算密集型,如金融分析;利用多核实现并发。
多线程用于IO密集型,如socket,爬虫,web。


1七、请分别简述threading.Condition、threading.event、threading.semaphore、的使用场景;

condition: 某些事件触发或达到特定的条件后才处理数据。 event: 用来通知线程有一些事情已发生,从而启动后继任务的开始。 semaphore: 为控制一个具备有限数量用户资源而设计。

 

 

1八、假设有一个名为threading_test.py的程序里有一个li = [1, 2, 3, 4]的列表,另有a,b两个函数分别往该列表中增长元素,
a函数须要修改li以前须要得到threading.Lock对象,b函数不须要,请问当线程t1执行a函数获取到Lock对象以后并无release该对象的状况下,
线程t2执行b函是否能够修改li,为何?

能够,线程的数据是共享的,a 函数虽然上了锁,没有释放。因为b 函数不须要上锁,就能够访问资源。

 

 

1九、简述你对Python GIL的理解;

GIL(global interpreter lock)全局解释器锁 GIL是CPython的一个概念,本质是一把互斥锁,将并发运行变成串行。 解释器的代码是全部线程共享的,因此垃圾回收线程也有可能访问到解释器的代码去执行。 所以须要有GIL锁,保证python解释器同一时间只能执行一个任务的代码。 GIL:解释器级别的锁(保护的是解释器级别的数据,好比垃圾回收的数据) Lock:应用程序的锁(保护用户本身开发的应用程序的数据)

 


20、请列举你知道的进程间通讯方式;
消息队列 管道 信号量 信号 共享内存 套接字


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

同步I/O,用户进程须要主动读写数据。 异步I/O,不须要主动读写数据,只须要读写数据完成的通知。

 

2二、什么是管道,若是两个进程尝试从管道的同一端读写数据,会出现什么状况?

管道:是两个进程间进行单向通讯的机制。因为管道传递数据的单向性。管道又称为半双工管道。 管道传递数据是单向性的,读数据时,写入管道应关闭。写数据时,读取管道应关闭。

 

2三、为何要使用线程池/进程池?
对服务端开启的进程数或线程数加以控制,让机器在一个本身能够承受的范围内运行,这就是进程池或线程池的用途.


2四、若是多个线程都在等待同一个锁被释放,请问当该锁对象被释放的时候,哪个线程将会得到该锁对象?
这个由操做系统的调度决定。

2五、import threading;s = threading.Semaphore(value=-1)会出现什么状况?

当threading.Semaphore(1) 为1时,表示只有一个线程可以拿到许可,其余线程都处于阻塞状态,直到该线程释放为止。 固然信号量不可能永久的阻塞在那里。信号量也提供了超时处理机制。若是传入了 -1,则表示无限期的等待。

 

 

2六、请将二进制数10001001转化为十进制;
10001001 = 1*10^7 + 1*10^3 + 1* 10^0 = 10001001


2七、某进程在运行过程当中须要等待从磁盘上读入数据,此时该进程的状态将发生什么变化?

一个程序有三种状态:运行态,阻塞态,就绪态; 遇到IO阻塞,进程从运行态转到阻塞态,cpu切走,保存当前状态;

 


2八、请问selectors模块中DefaultSelector类的做用是什么;

IO多路复用:select poll epoll select: 列表循环,效率低。windows 支持。 poll: 可接收的列表数据多,效率也不高。linux 支持。 epoll: 效率最高 异步操做 + 回调函数。linux 支持。 selectors 模块: sel=selectors.DefaultSelector() 自动根据操做系统选择select/poll/epoll

 

2九、简述异步I/O的原理;

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

 


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

 一般,进程之间彼此是彻底孤立的,惟一的通讯方式是队列或管道。但可使用两个对象来表示共享数据。其实,这些对象使用了共享内存(经过mmap模块)使访问多个进程成为可能。

 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对象,但不存在锁定。当所编写的程序必须一次性操做大量的数组项时,若是同时使用这种数据类型和用于同步的单独锁定(若是须要的话),性能将获得极大的提高。

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

    http://xiaorui.cc/2016/05/10/%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90multiprocessing%E7%9A%84value-array%E5%85%B1%E4%BA%AB%E5%86%85%E5%AD%98%E5%8E%9F%E7%90%86/

https://blog.csdn.net/qdx411324962/article/details/46810421

3一、请问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类的做用共享资源,manger的的优势是能够在poor进程池中使用,缺点是windows下环境下性能比较差,由于windows平台须要把Manager.list放在if name
='main'下,
而在实例化子进程时,必须把Manager对象传递给子进程,不然lists没法被共享,而这个过程会消耗巨大资源,所以性能不好。 参考:http:
//www.kaka-ace.com/python-multiprocessing-module-2-managers-first-profile/ https://blog.csdn.net/alvine008/article/details/24310939

 



3二、请说说你对multiprocessing模块中的Queue().put(), Queue.put_nowait(), Queue.get(), Queue.get_nowait()的理解;

q = Queue(3) 队列 先进先出 进程间通讯; 队列 = 管道 + 锁 q.put() q.put_nowait() # 无阻塞,当队列满时,直接抛出异常queue.Full q.get() q.get_nowait() # 无阻塞,当队列为空时,直接抛出异常queue.Empty

 

3三、什么是协程?使用协程与使用线程的区别是什么?

协程:单线程下的并发。协程是一种用户态的轻量级线程,即协程是由用户程序本身控制调度的。 1.python的线程是属于内核级别的,即由操做系统控制调度(如单线程遇到io或执行时间过长就会被迫交出cpu执行权限, 切换其余的线程运行) 2.单线程内开启协程,一旦遇到io,就会从应用程序级别(而非操做系统)控制切换,以此来提高效率 (!!非io操做的切换与效率无关)

 


3四、asyncio的实现原理是什么?

https://www.cnblogs.com/earendil/p/7411115.html 
Python异步编程:asyncio库和async/await语法 asyncio是Python 3.4 试验性引入的异步I/O框架,提供了基于协程作异步I/O编写单线程并发代码的基础设施。 其核心组件有事件循环(Event Loop)、协程(Coroutine)、任务(Task)、将来对象(Future)以及其余一些扩充和辅助性质的模块。 synchronous io: 作”IO operation”的时候会将process阻塞;”IO operation”是指真实的IO操做 blocking IO,non-blocking IO,IO multiplexing都属于synchronous IO这一类. asynchronous io: 当进程发起IO 操做以后,就直接返回不再理睬了,直到kernel发送一个信号, 告诉进程说IO完成。在这整个过程当中,进程彻底没有被block。异步io的实现会负责把数据从内核拷贝到用户空间。

2、编程题

一、请写一个包含10个线程的程序,主线程必须等待每个子线程执行完成以后才结束执行,每个子线程执行的时候都须要打印当前线程名、当前活跃线程数量以及当前线程名称;

from threading import Thread, currentThread, activeCount import time def task(n): print('线程名:%s %s'%(currentThread(), n )) time.sleep(2) print('当前活跃线程数量:%s'%activeCount()) if __name__ == '__main__': t_li = [] for i in range(10): t = Thread(target=task, args=(i, )) t.start() t_li.append(t) for t in t_li: t.join() print('主,---end---') 打印: 线程名:<Thread(Thread-1, started 6212)>   0 线程名:<Thread(Thread-2, started 780)>   1 线程名:<Thread(Thread-3, started 6616)>   2 线程名:<Thread(Thread-4, started 5344)>   3 线程名:<Thread(Thread-5, started 6352)>   4 线程名:<Thread(Thread-6, started 6724)>   5 线程名:<Thread(Thread-7, started 1168)>   6 线程名:<Thread(Thread-8, started 1540)>   7 线程名:<Thread(Thread-9, started 6812)>   8 线程名:<Thread(Thread-10, started 6824)>   9 当前活跃线程数量:11 当前活跃线程数量:10 当前活跃线程数量:9 当前活跃线程数量:8 当前活跃线程数量:7 当前活跃线程数量:6 当前活跃线程数量:5 当前活跃线程数量:4 当前活跃线程数量:4 当前活跃线程数量:4 主,---end---

 

 

二、请写一个包含10个线程的程序,并给每个子线程都建立名为"name"的线程私有变量,变量值为“Alex”;

from threading import Thread def task(name): print('%s is running'%name) #print('end---') if __name__ == '__main__': for i in range(10): t = Thread(target=task, args=('alex_%s'%i,) ) t.start() print('----主-----') 打印: alex_0 is running alex_1 is running alex_2 is running alex_3 is running alex_4 is running alex_5 is running alex_6 is running alex_7 is running alex_8 is running alex_9 is running ----主-----

 

三、请使用协程写一个消费者生产者模型;

def consumer(): while True: x = yield print('消费:', x) def producter(): c = consumer() next(c) for i in range(10): print('生产:', i) c.send(i) producter() 打印: 生产: 0 消费: 0 生产: 1 消费: 1 生产: 2 消费: 2 生产: 3 消费: 3 生产: 4 消费: 4 生产: 5 消费: 5 生产: 6 消费: 6 生产: 7 消费: 7 生产: 8 消费: 8 生产: 9 消费: 9

 


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

from threading import Thread,Event import time import datetime def task(): event.wait(10)i print('time:', datetime.datetime.now()) if __name__ == '__main__': event = Event() for i in range(10): t = Thread(target=task ) t.start() time.sleep(10) event.set() 打印: time: 2018-05-01 17:31:15.896462 time: 2018-05-01 17:31:15.897462 time: 2018-05-01 17:31:15.897462 time: 2018-05-01 17:31:15.897462 time: 2018-05-01 17:31:15.897462 time: 2018-05-01 17:31:15.897462 time: 2018-05-01 17:31:15.900462 time: 2018-05-01 17:31:15.900462 time: 2018-05-01 17:31:15.901462 time: 2018-05-01 17:31:15.901462

 

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

 ##用信号亮

from threading import Thread, Semaphore, currentThread import time sem = Semaphore(5) def task(): with sem: print('%s in'%currentThread().getName()) time.sleep(2) if __name__ == '__main__': for i in range(10): t = Thread(target=task, ) t.start() 打印:一开始只有5个子进程并行执行 Thread-1 in Thread-2 in Thread-3 in Thread-4 in Thread-5 in Thread-6 in Thread-8 in Thread-7 in Thread-9 in Thread-10 in

 

##用线程池

from concurrent.futures import ThreadPoolExecutor from threading import currentThread import os,time def task(): print('name:%s in pid:%s'%(currentThread().getName(), os.getpid())) time.sleep(2) if __name__ == '__main__': pool = ThreadPoolExecutor(5) for i in range(10): pool.submit(task, ) pool.shutdown(wait=True) 打印: name:<concurrent.futures.thread.ThreadPoolExecutor object at 0x0000000001E8FCF8>_0 in pid:9152 name:<concurrent.futures.thread.ThreadPoolExecutor object at 0x0000000001E8FCF8>_1 in pid:9152 name:<concurrent.futures.thread.ThreadPoolExecutor object at 0x0000000001E8FCF8>_2 in pid:9152 name:<concurrent.futures.thread.ThreadPoolExecutor object at 0x0000000001E8FCF8>_3 in pid:9152 name:<concurrent.futures.thread.ThreadPoolExecutor object at 0x0000000001E8FCF8>_4 in pid:9152 name:<concurrent.futures.thread.ThreadPoolExecutor object at 0x0000000001E8FCF8>_1 in pid:9152 name:<concurrent.futures.thread.ThreadPoolExecutor object at 0x0000000001E8FCF8>_0 in pid:9152 name:<concurrent.futures.thread.ThreadPoolExecutor object at 0x0000000001E8FCF8>_2 in pid:9152 name:<concurrent.futures.thread.ThreadPoolExecutor object at 0x0000000001E8FCF8>_3 in pid:9152 name:<concurrent.futures.thread.ThreadPoolExecutor object at 0x0000000001E8FCF8>_4 in pid:9152

 

六、写一个程序 ,包含一个名为hello的函数,函数的功能是打印字符串“Hello, World!”,该函数必须在程序执行30秒以后才开始执行(不能使用time.sleep());

from threading import Timer def hello(name): print('%s say '%name, 'Hello World!') if __name__ == '__main__': t = Timer(5, hello, args=('alex', )) t.start() 打印: alex say Hello World!

 


七、写一个程序,利用queue实现进程间通讯;

from multiprocessing import Process, current_process, Queue import time def consumer(q): while True: res = q.get() #接结果 if not res:break print('消费了:', res, '---', current_process().name) def producter(q): for i in range(5): print('生产:', i) time.sleep(1) q.put(i) if __name__ == '__main__': q = Queue() p1 = Process(target=producter, args=(q, )) p2 = Process(target=producter, args=(q, )) c1 = Process(target=consumer, args=(q, )) c2 = Process(target=consumer, args=(q, )) c3= Process(target=consumer, args=(q, )) p1.start() p2.start() c1.start() c2.start() c3.start() p1.join() p2.join() q.put(None) #None表明结束信号,有几个消费者来几个信号 q.put(None) #在主进程里边确保全部的生产者都生产结束以后才发结束信号 q.put(None) print('') 打印: 生产: 0 生产: 0 生产: 1 生产: 1 生产: 2 消费了: 1 --- Process-4 生产: 2 消费了: 1 --- Process-4 生产: 3 消费了: 2 --- Process-4 消费了: 2 --- Process-4 生产: 3 生产: 4 消费了: 3 --- Process-4 生产: 4 消费了: 3 --- Process-4 消费了: 4 --- Process-4 消费了: 4 --- Process-4

 

 

八、写一个程序,利用pipe实现进程间通讯;

from multiprocessing import Process, Pipe def task(conn): conn.send('hello world') conn.close() if __name__ == '__main__': parent_conn, child_conn = Pipe() p = Process(target=task, args=(child_conn, )) p.start() p.join() print(parent_conn.recv()) 打印: hello world

 

九、写一个程序,要求用户输入用户名和密码,要求密码长度很多于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()

 

十、写一个程序,使用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'))

 


十一、使用selectors模块建立一个处理客户端消息的服务器程序;

#server import socket import selectors sel = selectors.DefaultSelector() def accept(server_fileobj, mask): conn, addr = server_fileobj.accept() print(addr) sel.register(conn,selectors.EVENT_READ,read) def read(conn,mask): try: data = conn.recv(1024) if not data: print('closing..',conn) sel.unregister(conn) conn.close() return conn.send(data.upper()) except Exception: print('closeing...',conn) sel.unregister(conn) conn.close() server_fileobj = socket.socket(socket.AF_INET,socket.SOCK_STREAM) server_fileobj.bind(('127.0.0.1',8080)) server_fileobj.listen(5) server_fileobj.setblocking(False) sel.register(server_fileobj,selectors.EVENT_READ,accept) while True: events = sel.select() for sel_obj,mask in events: callback = sel_obj.data callback(sel_obj.fileobj,mask) ##client import socket client = socket.socket(socket.AF_INET,socket.SOCK_STREAM) client.connect(('127.0.0.1',8080)) while True: msg = input('>>>:').strip() if not msg:continue client.send(msg.encode('utf-8')) data = client.recv(1024) print(data.decode('utf-8'))

 

十二、使用socketserver建立服务器程序时,若是使用fork或者线程服务器,一个潜在的问题是,恶意的程序可能会发送大量的请求致使服务器崩溃,请写一个程序,避免此类问题;

# server socketserver 模块内部使用IO多路复用 和多进程/多线程 import socketserver class Handler(socketserver.BaseRequestHandler): def handle(self): 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) break

 

1三、请使用asyncio实现一个socket服务器端程序;

相关文章
相关标签/搜索