python基础学习20----线程
Process,LWP),是程序执行流的最小单元。一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。由于线程之间的相互制约,致使线程在运行中呈现出间断性。线程也有就绪、阻塞和运行三种基本状态。就绪状态是指线程具备运行的所有条件,逻辑上可以运行,在等待处理机;运行状态是指线程占有处理机正在运行;阻塞状态是指线程在等待一个事件(如某个信号量),逻辑上不可执行。每一个程序都至少有一个线程,若程序只有一个线程,那就是程序本身。线程是程序中一个单一的顺序控制流程。进程内有一个相对独立的、可调度的执行单元,是系统独立调度和分派CPU的基本单位指令运行时的程序的调度单位。在单个程序中同时运行多个线程完成不同的工作,称为多线程。
进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。 线程与进程的关系
<span style="color: #0000ff;">def<span style="color: #000000;"> func(msg):
<span style="color: #0000ff;">print<span style="color: #000000;">(msg) <span style="color: #0000ff;">print(<span style="color: #800000;">"<span style="color: #800000;">这是一个线程<span style="color: #800000;">"<span style="color: #000000;">) t=threading.Thread(target=func,args=(<span style="color: #800000;">"<span style="color: #800000;">hello world<span style="color: #800000;">"<span style="color: #000000;">,)) 通过继承类的方式创建线程 ==
</span><span style="color: #0000ff;">def</span><span style="color: #000000;"> run(self):
</span><span style="color: #008000;">#</span><span style="color: #008000;">这里是将threading.Thread中的run方法进行了重载</span>
<span style="color: #0000ff;">print</span>(<span style="color: #800000;">"</span><span style="color: #800000;">%s is %d</span><span style="color: #800000;">"</span>%<span style="color: #000000;">(self.name,self.age))
t =Mythread(<span style="color: #800000;">"<span style="color: #800000;">sfencs<span style="color: #800000;">",19<span style="color: #000000;">)t.start() 2.线程的并发 单个线程的创建基本没有意义,只是与主线程并发,现在我们看一下多个线程的并发 ===
</span><span style="color: #0000ff;">def</span><span style="color: #000000;"> run(self):
</span><span style="color: #0000ff;">print</span><span style="color: #000000;">(self.name)
time.sleep(self.second)
</span><span style="color: #0000ff;">print</span><span style="color: #000000;">(self.age)
t1 =Mythread(<span style="color: #800000;">"<span style="color: #800000;">sfencs<span style="color: #800000;">",19,2<span style="color: #000000;">)t2=Mythread(<span style="color: #800000;">"<span style="color: #800000;">Tom<span style="color: #800000;">",25,5<span style="color: #000000;">) t1.start() t2.start() 这里先同时打印sfencs和Tom,过了两秒打印19,又过3秒打印25.这说明这两个线程是并发的,如果是串行的那么会使用7秒完成 我们可以使用time模块计算时间 ===
</span><span style="color: #0000ff;">def</span><span style="color: #000000;"> run(self):
</span><span style="color: #0000ff;">print</span><span style="color: #000000;">(self.name)
time.sleep(self.second)
</span><span style="color: #0000ff;">print</span><span style="color: #000000;">(self.age)
time_begin =<span style="color: #000000;">time.time()t1 =Mythread(<span style="color: #800000;">"<span style="color: #800000;">sfencs<span style="color: #800000;">",5<span style="color: #000000;">)t1.start() t2.start() time_end=<span style="color: #000000;">time.time() <span style="color: #0000ff;">print(time_end-time_begin) ''' sfencsTom0.00103068351745605471925 ''' 这里出现一个问题,输出的时间是0.0010306835174560547,而且在年龄之前输出的 原因是计算时间的代码属于主线程,它与两个自己创建的线程并发,所以它提前完成了计算,为了解决这个办法,我们使用join()方法 3.join()
time_begin=t1=Mythread(<span style="color: #800000;">"<span style="color: #800000;">sfencs<span style="color: #800000;">",5<span style="color: #000000;">)
t1.start() t2.start() t1.join() t2.join() time_end=<span style="color: #000000;">time.time() <span style="color: #0000ff;">print(time_end-<span style="color: #000000;">time_begin) <span style="color: #800000;">'''<span style="color: #800000;"> sfencs Tom 19 25 5.001618146896362 <span style="color: #800000;">''' 这样就显然的看出程序并发节约了约2秒钟 除此之外join()方法还有一个参数为阻塞的时间,默认为一直阻塞 4.IO密集型任务和计算密集型任务 IO密集型任务就如上述的例子一样,有阻塞的状态,如sleep()或者等待相关信息,信号时会停用cpu的任务。IO密集型的任务在python中使用多线程能够很好的节约时间完成并发。 计算密集型任务没有等待状态,从上到下执行,没有任何等待 一个线程 </span><span style="color: #0000ff;">def</span><span style="color: #000000;"> run(self):
i</span>=<span style="color: #000000;">0
</span><span style="color: #0000ff;">while</span> i<100000000<span style="color: #000000;">:
i</span>+=1<span style="color: #000000;">
time_begin =<span style="color: #000000;">time.time()t1=<span style="color: #000000;">Mythread() t1.start() t1.join() time_end=<span style="color: #000000;">time.time() <span style="color: #0000ff;">print(time_end-time_begin)#6.194466590881348 两个线程 </span><span style="color: #0000ff;">def</span><span style="color: #000000;"> run(self):
i</span>=<span style="color: #000000;">0
</span><span style="color: #0000ff;">while</span> i<100000000<span style="color: #000000;">:
i</span>+=1<span style="color: #000000;">
time_begin =<span style="color: #000000;">time.time()t1=<span style="color: #000000;">Mythread() t2=<span style="color: #000000;">Mythread() t1.start() t2.start() t1.join() t2.join() time_end=<span style="color: #000000;">time.time() <span style="color: #0000ff;">print(time_end-time_begin)#11.998910427093506 可见计算密集型任务在python中并发并不能很好的节约时间,和串行差不多(在python以前版本中时间还会比串行多) 可是又有一个问题,我们的电脑不是有多核cpu吗,为什么不能同时两个cpu每个运行一个线程,那样时间就只有串行的一半啊?原因就是接下来讲的GIL 5.GIL 首先需要明确的一点是GIL并不是Python的特性,它是在实现Python解析器(CPython)时所引入的一个概念。就好比C++是一套语言(语法)标准,但是可以用不同的编译器来编译成可执行代码。有名的编译器例如GCC,INTEL C++,Visual C++等。Python也一样,同样一段代码可以通过CPython,PyPy,Psyco等不同的Python执行环境来执行。像其中的JPython就没有GIL。然而因为CPython是大部分环境下默认的Python执行环境。所以在很多人的概念里CPython就是Python,也就想当然的把GIL归结为Python语言的缺陷。所以这里要先明确一点:GIL并不是Python的特性,Python完全可以不依赖于GIL。 那么CPython实现中的GIL又是什么呢?GIL全称Global Interpreter Lock为了避免误导,我们还是来看一下官方给出的解释: In CPython,the global interpreter lock,or GIL,is a mutex that prevents multiple native threads from executing Python bytecodes at once. This lock is necessary mainly because CPython’s memory management is not thread-safe. (However,since the GIL exists,other features have grown to depend on the guarantees that it enforces.) ?GIL就像是一个防止多线程并发的全局锁,GIL的存在导致多线程无法很好的立即多核CPU的并发处理能力。python的多线程在多核CPU上,只对于IO密集型计算产生正面效果;而当有至少有一个CPU密集型线程存在,那么多线程效率会由于GIL而大幅下降。为了避免GIL的影响,可以使用多进程。 这里具体参考 6.守护线程setDaemon 当主线程完成时不需要某个子线程完全运行完就要退出程序,那么就可以将这个子线程设置为守护线程,setDaemon(True). =(time_end-
不会显示年龄的输出,因为主线程已经结束。 7.Lock锁 多线程中对同一资源进行处理,有可能会导致数据不安全 <span style="color: #0000ff;">def<span style="color: #000000;"> addNum():
<span style="color: #0000ff;">global<span style="color: #000000;"> num
<span style="color: #000000;"> <span style="color: #0000ff;">for i <span style="color: #0000ff;">in range(100<span style="color: #000000;">): <span style="color: #0000ff;">for t <span style="color: #0000ff;">in<span style="color: #000000;"> thread_list: <span style="color: #0000ff;">print(<span style="color: #800000;">'<span style="color: #800000;">final num:<span style="color: #800000;">',num )#final num: 92 这里运行结果并不是0,原因是多个线程在time.sleep()的时候同时拿到了num,所以num是同一个数,解决方法就是加锁 8.死锁与递归锁 ==<span style="color: #0000ff;">class<span style="color: #000000;"> MyThread(threading.Thread):
<span style="color: #0000ff;">def<span style="color: #000000;"> run(self): self.func1() self.func2() <span style="color: #0000ff;">def<span style="color: #000000;"> func1(self): mutexA.acquire() <span style="color: #0000ff;">print(<span style="color: #800000;">'<span style="color: #800000;">%s 拿到A锁<span style="color: #800000;">' %<span style="color: #000000;">self.name) mutexB.acquire() <span style="color: #0000ff;">print(<span style="color: #800000;">'<span style="color: #800000;">%s 拿到B锁<span style="color: #800000;">' %<span style="color: #000000;">self.name) mutexB.release() mutexA.release()
<span style="color: #0000ff;">if <span style="color: #800080;">name == <span style="color: #800000;">'<span style="color: #800000;">main<span style="color: #800000;">'<span style="color: #000000;">: Thread-1 拿到A锁Thread-1 拿到B锁Thread-1 拿到B锁Thread-2 拿到A锁 ?这里开了5个线程,可是却阻塞住了,原因是在Thread1拿到B锁,Thread2拿到A锁时,func2中在等待获得A锁,func1中在等待获得B锁,两者都在等待对方释放锁,造成了死锁,使得线程互相阻塞解决方法是使用递归锁Rlock =<span style="color: #0000ff;">class<span style="color: #000000;"> MyThread(threading.Thread):
<span style="color: #0000ff;">def<span style="color: #000000;"> run(self): self.func1() self.func2() <span style="color: #0000ff;">def<span style="color: #000000;"> func1(self): lock.acquire() <span style="color: #0000ff;">print(<span style="color: #800000;">'<span style="color: #800000;">%s 拿到A锁<span style="color: #800000;">' %<span style="color: #000000;">self.name) lock.acquire() <span style="color: #0000ff;">print(<span style="color: #800000;">'<span style="color: #800000;">%s 拿到B锁<span style="color: #800000;">' %<span style="color: #000000;">self.name) lock.release() lock.release()
<span style="color: #0000ff;">if <span style="color: #800080;">name == <span style="color: #800000;">'<span style="color: #800000;">main<span style="color: #800000;">'<span style="color: #000000;">: 在Python中为了支持在同一线程中多次请求同一资源,python提供了可重入锁RLock。 这个RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一个线程所有的acquire都被release,其他的线程才能获得资源 9.信号量
<div class="cnblogs_code"> =threading.Semaphore(3 ===
t=<span style="color: #000000;">[] 4.003796339035034 如果没有信号量来限制,那么程序完成的时间应该为2秒左右 10.条件变量同步 wait等待notify的通知,当接到通知后,会重新从if accquire()执行 ==
</span><span style="color: #0000ff;">def</span><span style="color: #000000;"> run(self):
</span><span style="color: #0000ff;">global</span><span style="color: #000000;"> count
</span><span style="color: #0000ff;">while</span><span style="color: #000000;"> True:
</span><span style="color: #0000ff;">if</span><span style="color: #000000;"> c.acquire():
</span><span style="color: #0000ff;">if</span> count>5<span style="color: #000000;">:
c.wait()
</span><span style="color: #0000ff;">else</span><span style="color: #000000;">:
count</span>+=1
<span style="color: #0000ff;">print</span>(self.name+<span style="color: #800000;">"</span><span style="color: #800000;">:生产了一件商品</span><span style="color: #800000;">"</span><span style="color: #000000;">)
c.notify()
c.release()
time.sleep(</span>0.5<span style="color: #000000;">)
<span style="color: #0000ff;">class<span style="color: #000000;"> consumer(threading.Thread):<span style="color: #0000ff;">def <span style="color: #800080;">init<span style="color: #000000;">(self): threading.Thread.<span style="color: #800080;">init<span style="color: #000000;">(self)
<span style="color: #0000ff;">for i <span style="color: #0000ff;">in range(2<span style="color: #000000;">):producer().start() <span style="color: #0000ff;">for i <span style="color: #0000ff;">in range(5<span style="color: #000000;">): consumer().start() 11.event 事件(event)用于线程间同步和通信。比如线程A要完成某一任务(event)线程B才能执行后面的代码 set() ?开始一个事件 wait() ?如果未设置set状态会一直等待,否则过 clear() 清除set状态 isSet() 是否设置set状态 <span style="color: #0000ff;">class<span style="color: #000000;"> interviewer(threading.Thread):
<span style="color: #0000ff;">def <span style="color: #800080;">init<span style="color: #000000;">(self):
<span style="color: #0000ff;">class<span style="color: #000000;"> interviewee1(threading.Thread):
event1=<span style="color: #000000;">threading.Event() t1 =<span style="color: #000000;"> interviewee1() 我能问你一个问题吗?你问吧我的问题刚才已经问完了行吧。。。 12.队列queue 说道多线程就不得不提到队列,python中的队列用到了Queue模块,该模块提供了同步的,安全的对序列,包括FIFO(先入先出)队列Queue,LIFO(后入先出)队列LifoQueue,和优先级队列PriorityQueue.这些队列都实现了锁原语,能够在多线程中直接使用。可以使用队列来实现线程间的通信 Queue.qsize() 返回队列的大小 Queue.empty() 如果队列为空,返回True,反之False Queue.full 与 maxsize 大小对应 Queue.get([block[,timeout]])获取队列,timeout等待时间 Queue.get_nowait() 相当Queue.get(False) Queue.put(item) 写入队列,timeout等待时间 Queue.put_nowait(item) 相当Queue.put(item,False) Queue.task_done() 在完成一项工作之后,Queue.task_done()函数向任务已经完成的队列发送一个信号 Queue.join() 实际上意味着等到队列为空,再执行别的操作 这个程序将之前的一个用队列改写的 == </span><span style="color: #0000ff;">def</span><span style="color: #000000;"> run(self):
</span><span style="color: #0000ff;">global</span><span style="color: #000000;"> count
</span><span style="color: #0000ff;">while</span><span style="color: #000000;"> True:
</span><span style="color: #0000ff;">if</span><span style="color: #000000;"> c.acquire():
</span><span style="color: #0000ff;">if</span><span style="color: #000000;"> count.full():
c.wait()
</span><span style="color: #0000ff;">else</span><span style="color: #000000;">:
count.put(</span>1<span style="color: #000000;">)
</span><span style="color: #0000ff;">print</span>(self.name+<span style="color: #800000;">"</span><span style="color: #800000;">:生产了一件商品</span><span style="color: #800000;">"</span><span style="color: #000000;">)
c.notify()
c.release()
time.sleep(</span>0.5<span style="color: #000000;">)
<span style="color: #0000ff;">class<span style="color: #000000;"> consumer(threading.Thread):<span style="color: #0000ff;">def <span style="color: #800080;">init<span style="color: #000000;">(self): threading.Thread.<span style="color: #800080;">init<span style="color: #000000;">(self)
<span style="color: #0000ff;">for i <span style="color: #0000ff;">in range(2<span style="color: #000000;">):producer().start() <span style="color: #0000ff;">for i <span style="color: #0000ff;">in range(5<span style="color: #000000;">): consumer().start() (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |