python-高级编程-03
【多进程与多线程】
调度 : 在传统计算机操作系统中 cpu的调度的基本单位是进程,随着线程的引入,线程变成操作系统的最小调度单位
而进程是作为资源的拥有单位。
并行:由于线程的引入 原先一个进程只能有一个并发 现在一个进程可以有多个线程并行执行,
早起的httpserver 都是通过线程来解决服务器的并发 比起之前用fork子进程来处理并发效率有很大的提升。
这一切得益于线程可以用进程更低的代价实现并发。
共享 :一般linux线程会让线程继承或共享如下资源
。进程的共有数据内存
。 进程所打开的文件描述符
。 信号的处理器
。 进程用户的ID和进程组ID
隔离:
。线程ID ,在linux中 线程和进程共享ID空间
。寄存器的值
。线程的栈
。 优先级 linux的系统设计使得线程和进程除了在某些资源的共享和隔离有差异外,几乎是一视同仁的,他们可以有不同观点priority
我们在选择多进程还是多线程的时候需要根据业务场景使用。他们特性就是共享和隔离的区别。
【Linux的进程】
Linux在linus设计的时候的定位就是一个多任务的操作系统,从linux出的第一个版本的时候 就有了 进程的概念,
线程的产生是为了解决并发问题,线程的定位也就是更小更轻的进程
一些问题:
为什么不能一味的开线程解决并发问题?
#线程的上下文切换所带来的消耗,开的线程越多,在上下文切换过程中消耗就越大,核心就是内存根不上cpu的速度。
上下文切换: 在cpu还是单核的时候,计算机操作系统就已经实现了多任务系统,但是你要知道 单核的cpu在同一时间段内
只能执行某一个进程的某一个指令,为了达到多任务的执行效果,linux把cpu的时间切成大小不到等的时间
片,通过内核调度算法,让进程一个个上去跑,由于切换时间片的时间非常短,在我们人类看来 计算机在同时执行多个程序。
那么这个是如果实现的呢,如果程序到了时间片结束之后还没有完成它的工作,那么操作系统会把这个程序,以及其依赖的数据
都保存在内存里,然后回到进程的队列里。保存现场是需要代价的,这将极大的影响cpu的分支预测,影响系统性能,
所以频繁的上下文切换,是我们及其避免的。
【协程】
协程就是用户自己在进程中控制多任务的栈,尽可能的不让进程由于外部中断或者IO的等待丧失CPU调度的时间片,从而在进程内部实现并发。
【内存与守护进程】
程序运行时的内存,也就是我们在用户状态能看到的内存地址,都不是物理内存的地址,现代操作系统都会在物理内存上做一层,内存映射,每个
进程内的内存空间都是独立的
守护进程的特点:
1 后台运行,也不占用conscle的前面 也就是bash里运行程序后面加个&
2 成为 process group leader 守护进程的父进程是init的那个进程
3 成为 session leader 一个ssh登陆会启动一个bash bash会fork很多子进程,这些进程轮流接受tty输出,这都是一个session session leader就是一队进程的父进程
4 fork 一次到两次 因为linux父进程只对子进程负责,fork两次可以保证不影响正在执行的程序,直接交给init
5 chdir到/ 防止占用别的路径的 working dir的id 导致block不能
6 需要重置umask 方式后续子进程继承非默认umask造成不可控问题
7 处理标准输入输出 错误输出(0,1,2) 重定向stdout stderr stdin 防止tty中断后的broken pipe信号
8 日志 输入重定向后,需要有办法反映内部情况
--关于僵尸进程--父进程派生出子进程 如果子进程挂了 但是一般进程 父进程会拿着子进程的pid 调用wait,如果父进程对子进程没有处理,这个这个时候就会变成僵尸进程
如果父进程也挂了init会回收僵尸进程 而僵尸进程存的就是僵尸进程退出的退出码。
【用python写一个守护进程】
#!/usr/bin/env python #coding=utf-8 import os,sys import time ''' 使用python写个守护进程 ''' def daemonzie(stdin='/dev/null',stdout='/dev/null',stderr='/dev/null'): try: pid = os.fork() if pid >0: sys.exit(0) except OSError ,e: sys.stderr.write("fork #2 failed (%d) %s\n "%(e.errno,e.strerror)) sys.exit(1) os.chdir('/')# chdir到/ 防止占用别的路径的 working dir的id 导致block不能 os.umask(0)#需要重置umask 方式后续子进程继承非默认umask造成不可控问题 os.setsid()#成为 process group leader 守护进程的父进程是init的那个进程 try: pid = os.fork() if pid >0: sys.exit(0) except OSError,e: sys.stderr.write("fork #2 failed (%d) %s\n " % (e.errno, e.strerror)) sys.exit(1) for i in sys.stdout,sys.stderr:i.flush() si = open(stdin,'r+') so = open(stdout,'a+') se = open(stderr,'a+',0) os.dup2(si.fileno(),sys.stdin.fileno()) os.dup2(so.fileno(), sys.stdin.fileno()) os.dup2(se.fileno(), sys.stdin.fileno()) def main(): import time sys.stdout.write('Daemon started with pid %s\n'%os.getpid()) sys.stdout.write('Daemon stdout output\n') sys.stderr.write('Daemon stderr output\n') c = 0 while 1: sys.stdout.write('%d:%s\n'%(c,time.ctime())) sys.stdout.flush() c + c+1 time.sleep(1) if __name__ == '__main__': daemonzie('/dev/null','/tmp/daemon_stdout.log','/tmp/daemon_error.log') main()
说白了 我们写一个 while 1 :print 'xxxx' 这种其实就是守护进程的基础 我们需要对一些东西进行处理后 才能形成一个正常的守护进程
【多线程实例】
1>
#!/usr/bin/env python #coding:utf-8 import thread def f(name): #定义线程函数 print 'this is '+name if __name__ == '__maim__': thread.start_new_thread(f,("thread1",)) while 1: pass
2>
#/usr/bin/env python #coding:utf-8 import threading class Th(threading.Thread): def __init__(self,name): threading.Thread.__init__(self) self.t_name = name def run(self): print "this is " + self.t_name if __name__ == '__main__': thread1= Th("Thread1") thread1.start()
threading.Thread 类的可继承函数
getName() 获得线程对象名称
setName() 设置线程对象名称
join() 等待调用的线程件数后再运行的命令
setDaemin(bool)阻塞模式
True 父线程不等待子线程的结束
False 等待默认为等待
isDaemon() 判断子线程是否和父线程一起结束 即setDaemon() 设置的值
isAlive()判断线程是否在运行
import threading import time class my_therad(threading.Thread): def __init__(self,thread_name): threading.Thread.__init__(self) self.setName=(thread_name) def run(self): print 'this is thread'+ self.getName() for i in range(5): time.sleep(1) print(str(i)) print self.getName()+'is over' if __name__ == '__main__': thread1 = my_therad('T1') thread1.start() #thread1.join() print 'main thread is over'
这里如果加了join()
this is threadThread-1 0 1 2 3 4 Thread-1is over main thread is over
如果没加
this is threadThread-1 main thread is over 0 1 2 3 4 Thread-1is over
加了join 主线程会等待子线程结束返回之后才会执行
-------------------------------------------------------------------
import threading import time class my_therad(threading.Thread): def __init__(self,thread_name): threading.Thread.__init__(self) self.setName=(thread_name) def run(self): print 'this is thread'+ self.getName() for i in range(5): time.sleep(1) print(str(i)) print self.getName()+'is over' if __name__ == '__main__': thread1 = my_therad('T1') thread1.setDaemon(True) thread1.start() #thread1.setDaemon(True) #thread1.join() print 'main thread is over'
如果setDaemon 在start之前那么 主线程不会等待子线程,直接结束了
输出:
起多个子线程
if __name__ == '__main__': for i in range(3): t = my_therad(str(i)) t.start() print 'main thread is over'
【线程锁】
import threading import time class Th(threading.Thread): def __init__(self,thread_name): threading.Thread.__init__(self) self.setName(thread_name) def run(self): threadLock.acquire() print "this is thread "+self.getName() for i in range(3): time.sleep(1) print str(i) print self.getName()+' is over' threadLock.release() if __name__ == '__main__': threadLock = threading.Lock() thread1 = Th('Thread_1') thread2 = Th('Thread_2') thread1.start() thread2.start()
如果加上锁输出就是这样
this is thread Thread_1 0 1 2 Thread_1 is over this is thread Thread_2 0 1 2 Thread_2 is over
如果不加
this is thread Thread_1 this is thread Thread_2 0 0 1 1 2 Thread_2 is over 2 Thread_1 is over
在thread中 优先会让之前拿到锁的人去拿锁 这样可以保证cpu分支预测的成功率
=======================================================================================================
#!/usr/bin/env python #coding:utf-8 ##################### # time:2017-08-11 # ##################### ''' this class is Daemon class ''' import os,sys,time,atexit from signal import SIGTERM class Daemon(): def __init__(self,pidfile = 'nbmon.pid',stdin='/dev/null',stdout='nbmon.log',stderr ='nbmon.log'): self.stdin = stdin self.stdout =stdout self.stderr = stderr self.pidfile = pidfile def daeminize(self): try: pid = os.fork() if pid > 0: sys.exit(0) except OSError ,e: sys.stderr.write("fork #1 failed:%d(%s)"(e.errno,e.strerror)) sys.exit(1) os.chdir('/') os.setsid() os.umask(0) try: pid = os.fork() if pid > 0: sys.exit(0) except OSError ,e: sys.stderr.write("fork #2 failed:%d(%s)"(e.errno,e.strerror)) sys.exit(1) sys.stdout.flush() sys.stderr.flush() si= file(self.stdin,'r') so= file(self.stdout,'a+') se = file(self.stderr,'a+',0) os.dup2(si.fileno(),sys.stdin.fileno()) os.dup2(si.fileno(),sys.stdout.fileno()) os.dup2(si.fileno(),sys.stderr.fileno()) atexit.regoster(self.delpid) pid = str(os.getpid()) file(self.pidfile,'w+'.write("%s\n"%pid)) def delpid(self): os.remove(self.pidfile) def start(self): try: pf = file(self.pidfile,'r') pid = int(pf.read().strip()) pf.close() except IOError,e: pid = None if pid : message = "pidfile %s alreadly exit,Daemon is running\n" sys.stderr.write(message % self.pidfile) sys.exit(1) self.daemonize() self.run() def stop(self): try: pf = file(self.pidfile,'r') pid = int(pf.read().strip()) pf.close except IOError: pid = None if not pid: message = "pidfile %s does not exitst,Daemon is running\n" sys.stderr.write(message % self.pidfile) return try: while 1: os.kill(pid,STGTERM) time.sleep(0.1) except OSError,err: err = str(err) if err.find("No such process") > 0 : if os.path.exists(self.pidfile): os.remove(self.pidfile) else: print srt(err) sys.exit(1) def restart(self): self.stop() self.start() def run(self): pass
#!/usr/bin/env python #coding:utf-8 from daemon import Daemon import socket import time html= """HTTP/1.1 200 OK\r\nContent-Type: image/jpeg\r\nConnection: close\r\nContent-Length: """ html404 = """HTTP/1.1 404 Not Found\r\nContent-Type: text/html\r\nContent-Length: 13\r\n\r\n<h1>404 </h1>""" class agentD(Daemon): def run(self): listen_fd = socket.socket(socket.AF_INET,socket.SOCK_STREAM, 0) listen_fd.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) listen_fd.bind(("0.0.0.0",9085)) listen_fd.listen(10) while True: conn,addr = listen_fd.accept() print "coming",conn,addr read_data = conn.recv(10000) try: pic_name = read_data.split(" ")[1][1:] print pic_name,'*********' with file(pic_name) as f: pic_content = f.read() lengths = len(pic_content) print lengths,"####" html_resp = html html_resp += "%d\r\n\r\n" % (lengths) print html_resp html_resp += pic_content except: print "404 occur" html_resp = html404 while len(html_resp)>0: sent_cnt = conn.send(html_resp) print "sent:",sent_cnt html_resp = html_resp[sent_cnt:] conn.close() if __name__ == '__main__': agentd = agentD(pidfile = 'agent.pid',stdout='agent.log',stderr ='agent.log') agentd.run()