Resourcehtml
1、进程(process)python
狭义定义:进程就是一段程序的执行过程。编程
广义定义:进程是一个具备必定独立功能的程序关于某个数据集合的一次运行活动。它是操做系统动态执行的基本单元,在传统的操做系统中,进程既是基本的分配单元,也是基本的执行单元。网络
简单的来说进程的概念主要有两点:多线程
第一,进程是一个实体。每个进程都有它本身的地址空间,通常状况下,包括:并发
文本区域(text region),本区域存储处理器执行的代码;app
数据区域(data region),存储变量和进程执行期间使用的动态分配的内存;socket
堆栈(stack region),存储着活动过程调用的指令和本地变量。高并发
第二,进程是一个“执行中的程序”。程序是一个没有生命的实体,只有处理器赋予程序生命时,它才能成为一个活动的实体,咱们称其为进程。ui
进程状态:运行、阻塞、挂起阻塞、就绪、挂起就绪
就绪状态其实就是获取了出cpu外的全部资源,只要处理器分配资源就能够立刻执行。就绪状态有排队序列什么的,排队原则再也不赘述。运行态就是得到了处理器分配的资源,程序开始执行。阻塞态,当程序条件不够时候,须要等待条件知足时候才能执行,如等待i/o操做时候,此刻的状态就叫阻塞态。
状态之间的转换:
(1)准备就绪的进程,被CPU调度执行,变成运行态;
(2)运行中的进程,进行I/O请求或者不能获得所请求的资源,变成阻塞态;
(3)运行中的进程,进程执行完毕(或时间片已到),变成就绪态;
(4)将阻塞态的进程挂起,变成挂起阻塞态,当致使进程阻塞的I/O操做在用户重启进程前完成(称之为唤醒),挂起阻塞态变成挂起就绪态,当用户在I/O操做结束以前重启进程,挂起阻塞态变成阻塞态;
(5)将就绪(或运行)中的进程挂起,变成挂起就绪态,当该进程恢复以后,挂起就绪态变成就绪态;
2、程序
提及进程,就不得不说下程序。先看定义:程序是指令和数据的有序集合,其自己没有任何运行的含义,是一个静态的概念。而进程则是在处理机上的一次执行过程,它是一个动态的概念。这个不难理解,其实进程是包含程序的,进程的执行离不开程序,进程中的文本区域就是代码区,也就是程序。
3、线程
一般在一个进程中能够包含若干个线程,固然一个进程中至少有一个线程,否则没有存在的意义。线程能够利用进程所拥有的资源,在引入线程的操做系统中,一般都是把进程做为分配资源的基本单位,而把线程做为独立运行和独立调度的基本单位,因为线程比进程更小,基本上不拥有系统资源,故对它的调度所付出的开销就会小得多,能更高效的提升系统多个程序间并发执行的程度。
线程是进程中执行运算的最小单位,是进程中的一个实体,是被系统独立调度和分派的基本单位,线程本身不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的所有资源。一个线程能够建立和撤消另外一个线程,同一进程中的多个线程之间能够并发执行。
(1)易于调度。
(2)提升并发性。经过线程可方便有效地实现并发性。进程可建立多个线程来执行同一程序的不一样部分。
(3)开销少。建立线程比建立进程要快,所需开销不多
4、多线程
在一个程序中,这些独立运行的程序片断叫做“线程”(Thread),利用它编程的概念就叫做“多线程处理”。多线程是为了同步完成多项任务,不是为了提升运行效率,而是为了提升资源使用效率来提升系统的效率。线程是在同一时间须要完成多项任务的时候实现的。
最简单的比喻多线程就像火车的每一节车箱,而进程则是火车。车箱离开火车是没法跑动的,同理火车也不可能只有一节车箱。多线程的出现就是为了提升效率。
(1)一个线程只能属于一个进程,而一个进程能够有多个线程,但至少有一个线程。
(2)资源分配给进程,同一进程的全部线程共享该进程的全部资源。
(3)处理机分给线程,即真正在处理机上运行的是线程
(4)线程在执行过程当中,须要协做同步。不一样进程的线程间要利用消息通讯的办法实现同步。线程是指进程内的一个执行单元,也是进程内的可调度实体.
(1)调度:线程做为调度和分配的基本单位,进程做为拥有资源的基本单位
(2)并发性:不只进程之间能够并发执行,同一个进程的多个线程之间也可并发执行
(3)拥有资源:进程是拥有资源的一个独立单位,线程不拥有系统资源,但能够访问隶属于进程的资源.
(4)系统开销:在建立或撤消进程时,因为系统都要为之分配和回收资源,致使系统的开销明显大于建立或撤消线程时的开销。
(1)管道(pipe)及有名管道(named pipe):管道可用于具备亲缘关系的父子进程间的通讯,有名管道除了具备管道所具备的功能外,它还容许无亲缘关系进程间的通讯。
(2)信号(signal):信号是在软件层次上对中断机制的一种模拟,它是比较复杂的通讯方式,用于通知进程有某事件发生,一个进程收到一个信号与处理器收到一个中断请求效果上能够说是一致的。
(3)消息队列(message queue):消息队列是消息的连接表,它克服了上两种通讯方式中信号量有限的缺点,具备写权限得进程能够按照必定得规则向消息队列中添加新信息;对消息队列有读权限得进程则能够从消息队列中读取信息。
(4)共享内存(shared memory):能够说这是最有用的进程间通讯方式。它使得多个进程能够访问同一块内存空间,不一样进程能够及时看到对方进程中对共享内存中数据得更新。这种方式须要依靠某种同步操做,如互斥锁和信号量等。
(5)信号量(semaphore):主要做为进程之间及同一种进程的不一样线程之间得同步和互斥手段。
(6)套接字(socket):这是一种更为通常得进程间通讯机制,它可用于网络中不一样机器之间的进程间通讯,应用很是普遍。
当有多个线程的时候,常常须要去同步这些线程以访问同一个数据或资源。例如,假设有一个程序,其中一个线程用于把文件读到内存,而另外一个线程用于统计文件中的字符数。固然,在把整个文件调入内存以前,统计它的计数是没有意义的。可是,因为每一个操做都有本身的线程,操做系统会把两个线程看成是互不相干的任务分别执行,这样就可能在没有把整个文件装入内存时统计字数。为解决此问题,你必须使两个线程同步工做。
所谓同步,是指散步在不一样进程之间的若干程序片段,它们的运行必须严格按照规定的某种前后次序来运行,这种前后次序依赖于要完成的特定的任务。若是用对资源的访问来定义的话,同步是指在互斥的基础上(大多数状况),经过其它机制实现访问者对资源的有序访问。在大多数状况下,同步已经实现了互斥,特别是全部写入资源的状况一定是互斥的。少数状况是指能够容许多个访问者同时访问资源。
所谓互斥,是指散布在不一样进程之间的若干程序片段,当某个进程运行其中一个程序片断时,其它进程就不能运行它们之中的任一程序片断,只能等到该进程运行完这个程序片断后才能够运行。若是用对资源的访问来定义的话,互斥某一资源同时只容许一个访问者对其进行访问,具备惟一性和排它性。但互斥没法限制访问者对资源的访问顺序,即访问是无序的。
import threading import time def show(arg): time.sleep(1) print 'thread'+str(arg) for i in range(10): t = threading.Thread(target=show, args=(i,)) t.start() print 'main thread stop'
import time import threading def run(n): print('[%s]------running----\n' % n) time.sleep(2) print('--done--') def main(): for i in range(5): t = threading.Thread(target=run,args=[i,]) #time.sleep(1) t.start() t.join(1) print('starting thread', t.getName()) m = threading.Thread(target=main,args=[]) m.setDaemon(True) #将主线程设置为Daemon线程,它退出时,其它子线程会同时退出,无论是否执行完任务 m.start() #m.join(timeout=2) print("---main thread done----")
一个进程下能够启动多个线程,多个线程共享父进程的内存空间,也就意味着每一个线程能够访问同一份数据,此时,若是2个线程同时要修改同一份数据,会出现什么情况?
import time import threading def addNum(): global num #在每一个线程中都获取这个全局变量 print('--get num:',num ) time.sleep(1) num -=1 #对此公共变量进行-1操做 num = 100 #设定一个共享变量 thread_list = [] for i in range(100): t = threading.Thread(target=addNum) t.start() thread_list.append(t) for t in thread_list: #等待全部线程执行完毕 t.join() print('final num:', num )
正常来说,这个num结果应该是0, 但在python 2.7上多运行几回,会发现,最后打印出来的num结果不老是0,为何每次运行的结果不同呢? 哈,很简单,假设你有A,B两个线程,此时都 要对num 进行减1操做, 因为2个线程是并发同时运行的,因此2个线程颇有可能同时拿走了num=100这个初始变量交给cpu去运算,当A线程去处完的结果是99,但此时B线程运算完的结果也是99,两个线程同时CPU运算的结果再赋值给num变量后,结果就都是99。那怎么办呢? 很简单,每一个线程在要修改公共数据时,为了不本身在还没改完的时候别人也来修改此数据,能够给这个数据加一把锁, 这样其它线程想修改此数据时就必须等待你修改完毕并把锁释放掉后才能再访问此数据。
注:不要在3.x上运行,不知为何,3.x上的结果老是正确的,多是自动加了锁
加锁版本:
import time import threading def addNum(): global num #在每一个线程中都获取这个全局变量 print('--get num:',num ) time.sleep(1) lock.acquire() #修改数据前加锁 num -=1 #对此公共变量进行-1操做 lock.release() #修改后释放 num = 100 #设定一个共享变量 thread_list = [] lock = threading.Lock() #生成全局锁 for i in range(100): t = threading.Thread(target=addNum) t.start() thread_list.append(t) for t in thread_list: #等待全部线程执行完毕 t.join() print('final num:', num )
说白了就是在一个大锁中还要再包含子锁
import threading,time def run1(): print("grab the first part data") lock.acquire() global num num +=1 lock.release() return num def run2(): print("grab the second part data") lock.acquire() global num2 num2+=1 lock.release() return num2 def run3(): lock.acquire() res = run1() print('--------between run1 and run2-----') res2 = run2() lock.release() print(res,res2) if __name__ == '__main__': num,num2 = 0,0 lock = threading.RLock() for i in range(10): t = threading.Thread(target=run3) t.start() while threading.active_count() != 1: print(threading.active_count()) else: print('----all threads done---') print(num,num2)
互斥锁 同时只容许一个线程更改数据,而Semaphore是同时容许必定数量的线程更改数据 ,好比厕全部3个坑,那最多只容许3我的上厕所,后面的人只能等里面有人出来了才能再进去。
import threading,time def run(n): semaphore.acquire() time.sleep(1) print("run the thread: %s\n" %n) semaphore.release() if __name__ == '__main__': num= 0 semaphore = threading.BoundedSemaphore(5) #最多容许5个线程同时运行 for i in range(20): t = threading.Thread(target=run,args=(i,)) t.start() while threading.active_count() != 1: pass #print threading.active_count() else: print('----all threads done---') print(num)
Python提供了Event对象用于线程间通讯,它是由线程设置的信号标志,若是信号标志位真,则其余线程等待直到信号接触。
Event对象实现了简单的线程通讯机制,它提供了设置信号,清楚信号,等待等用于实现线程间的通讯。
Event对象wait的方法只有在内部信号为真的时候才会很快的执行并完成返回。当Event对象的内部信号标志位假时,则wait方法一直等待到其为真时才返回。
使用Event的set()方法能够设置Event对象内部的信号标志为真。Event对象提供了isSet()方法来判断其内部信号标志的状态。当使用event对象的set()方法后,isSet()方法返回真
使用Event对象的clear()方法能够清除Event对象内部的信号标志,即将其设为假,当使用Event的clear方法后,isSet()方法返回假