线程是进程中执行运算的最小单位,是进程中的一个实体,是被系统独立调度和分派的基本单位,线程本身不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的所有资源。一个线程能够建立和撤消另外一个线程,同一进程中的多个线程之间能够并发执行。
************************python
(1)易于调度。算法
(2)提升并发性。经过线程可方便有效地实现并发性。进程可建立多个线程来执行同一程序的不一样部分。网络
(3)开销少。建立线程比建立进程要快,所需开销不多多线程
状态:运行、阻塞、挂起阻塞、就绪、挂起就绪并发
状态之间的转换:app
(1)准备就绪的进程,被CPU调度执行,变成运行态;socket
(2)运行中的进程,进行I/O请求或者不能获得所请求的资源,变成阻塞态;高并发
(3)运行中的进程,进程执行完毕(或时间片已到),变成就绪态;ui
(4)将阻塞态的进程挂起,变成挂起阻塞态,当致使进程阻塞的I/O操做在用户重启进程前完成(称之为唤醒),挂起阻塞态变成挂起就绪态,当用户在I/O操做结束以前重启进程,挂起阻塞态变成阻塞态;操作系统
(5)将就绪(或运行)中的进程挂起,变成挂起就绪态,当该进程恢复以后,挂起就绪态变成就绪态;
** 进程和线程的关系:**
(1)一个线程只能属于一个进程,而一个进程能够有多个线程,但至少有一个线程。
(2)资源分配给进程,同一进程的全部线程共享该进程的全部资源。
(3)处理机分给线程,即真正在处理机上运行的是线程
(4)线程在执行过程当中,须要协做同步。不一样进程的线程间要利用消息通讯的办法实现同步。线程是指进程内的一个执行单元,也是进程内的可调度实体.
进程与线程的区别:
(1)调度:线程做为调度和分配的基本单位,进程做为拥有资源的基本单位
(2)并发性:不只进程之间能够并发执行,同一个进程的多个线程之间也可并发执行
(3)拥有资源:进程是拥有资源的一个独立单位,线程不拥有系统资源,但能够访问隶属于进程的资源.
(4)系统开销:在建立或撤消进程时,因为系统都要为之分配和回收资源,致使系统的开销明显大于建立或撤消线程时的开销。
(1)管道(pipe)及有名管道(named pipe):管道可用于具备亲缘关系的父子进程间的通讯,有名管道除了具备管道所具备的功能外,它还容许无亲缘关系进程间的通讯。
(2)信号(signal):信号是在软件层次上对中断机制的一种模拟,它是比较复杂的通讯方式,用于通知进程有某事件发生,一个进程收到一个信号与处理器收到一个中断请求效果上能够说是一致的。
(3)消息队列(message queue):消息队列是消息的连接表,它克服了上两种通讯方式中信号量有限的缺点,具备写权限得进程能够按照必定得规则向消息队列中添加新信息;对消息队列有读权限得进程则能够从消息队列中读取信息。
(4)共享内存(shared memory):能够说这是最有用的进程间通讯方式。它使得多个进程能够访问同一块内存空间,不一样进程能够及时看到对方进程中对共享内存中数据得更新。这种方式须要依靠某种同步操做,如互斥锁和信号量等。
(5)信号量(semaphore):主要做为进程之间及同一种进程的不一样线程之间得同步和互斥手段。
(6)套接字(socket):这是一种更为通常得进程间通讯机制,它可用于网络中不一样机器之间的进程间通讯,应用很是普遍。
当有多个线程的时候,常常须要去同步这些线程以访问同一个数据或资源。例如,假设有一个程序,其中一个线程用于把文件读到内存,而另外一个线程用于统计文件中的字符数。固然,在把整个文件调入内存以前,统计它的计数是没有意义的。可是,因为每一个操做都有本身的线程,操做系统会把两个线程看成是互不相干的任务分别执行,这样就可能在没有把整个文件装入内存时统计字数。为解决此问题,你必须使两个线程同步工做。
所谓同步,是指散步在不一样进程之间的若干程序片段,它们的运行必须严格按照规定的某种前后次序来运行,这种前后次序依赖于要完成的特定的任务。若是用对资源的访问来定义的话,同步是指在互斥的基础上(大多数状况),经过其它机制实现访问者对资源的有序访问。在大多数状况下,同步已经实现了互斥,特别是全部写入资源的状况一定是互斥的。少数状况是指能够容许多个访问者同时访问资源。
所谓互斥,是指散布在不一样进程之间的若干程序片段,当某个进程运行其中一个程序片断时,其它进程就不能运行它们之中的任一程序片断,只能等到该进程运行完这个程序片断后才能够运行。若是用对资源的访问来定义的话,互斥某一资源同时只容许一个访问者对其进行访问,具备惟一性和排它性。但互斥没法限制访问者对资源的访问顺序,即访问是无序的。
#示例 #!/usr/bin/env python # -*- coding:utf-8 -*- 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'
上述代码建立了10个“前台”线程,而后控制器就交给了CPU,CPU根据指定算法进行调度,分片执行指令。
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 )
<线程锁与gil的区别> :
说白了就是在一个大锁中还要再包含子锁
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()方法返回假