Py之MT:Multithreaded的简介、引入、使用方法之详细攻略(一)-阿里云开发者社区

开发者社区> 一个处女座的程序猿> 正文

Py之MT:Multithreaded的简介、引入、使用方法之详细攻略(一)

简介: Py之MT:Multithreaded的简介、引入、使用方法之详细攻略
+关注继续查看

Multithreaded的简介


       多线程(英语:multithreading),是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能。具有这种能力的系统包括对称多处理机、多核心处理器以及芯片级多处理(Chip-level multithreading)或同时多线程(Simultaneous multithreading)处理器。 [1]  在一个程序中,这些独立运行的程序片段叫作“线程”(Thread),利用它编程的概念就叫作“多线程处理(Multithreading)”。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程(台湾译作“执行绪”),进而提升整体处理性能。

     在计算机编程中,一个基本的概念就是同时对多个任务加以控制。许多程序设计问题都要求程序能够停下手头的工作,改为处理其他一些问题,再返回主进程。可以通过多种途径达到这个目的。


1、多线程类似于同时执行多个不同程序,多线程运行有如下优点:

(1)、使用线程可以把占据长时间的程序中的任务放到后台去处理。

用户界面可以更加吸引人,这样比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的进度

(2)、程序的运行速度可能加快

在一些等待的任务实现上如用户输入、文件读写和网络收发数据等,线程就比较有用了。在这种情况下我们可以释放一些珍贵的资源如内存占用等等。

      线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。

每个线程都有他自己的一组CPU寄存器,称为线程的上下文,该上下文反映了线程上次运行该线程的CPU寄存器的状态。

指令指针和堆栈指针寄存器是线程上下文中两个最重要的寄存器,线程总是在进程得到上下文中运行的,这些地址都用于标志拥有线程的进程地址空间中的内存。

(1)、线程可以被抢占(中断)。

(2)、在其他线程正在运行时,线程可以暂时搁置(也称为睡眠) -- 这就是线程的退让。

2、什么是线程?

       线程(亦称为轻量级进程)跟进程有些相似,不同的是:所有的线程运行在同一个进程中,共享相同的运行环境。它们可以被想象成是在主进程或“主线程”中并行运行的“迷你进程”。

       线程有开始,顺序执行和结束三部分。它有一个自己的指令指针,记录自己运行到什么地方。线程的运行可能被抢占(中断)或暂时的被挂起(睡眠),让其他线程运行,这叫做让步。

       一个进程中的各个线程之间共享同一片数据空间,所以线程之间可以比进程之间更方便地共享数据以及相互通讯。线程一般都是并发执行的,正是由于这种并行和数据共享的机制使得多个任务的合作变成可能。

       实际上,在单CPU的系统中,真正的并发是不可能的,每个线程会被安排成每次只运行一小会,然后就把CPU让出来,让其他的线程去运行。在进程的整个运行过程中,每个线程都只做自己的事,在需要的时候跟其他的线程共享运行的结果。

       当然,这样的共享并不是完全没有危险的。如果多个线程共同访问同一片数据,则由于数据访问的顺序不同,有可能导致数据结果的不一致的问题,即竞态条件(race condition)。同样,大多数线程库都带有一些列的同步原语,来控制线程的执行和数据的访问。

       另一个需要注意的是由于有的函数会在完成之前阻塞住,在没有特别为多线程做修改的情况下,这种“贪婪”的函数会让CPU的时间分配有所倾斜,导致各个线程分配到的运行时间可能不尽相同,不尽公平。





Multithreaded的引入


1.为什么引入多线程编程?

       在多线程(Multithreaded,MT)编程出现之前,电脑程序的运行由一个执行序列组成,执行序列按顺序在主机的中央处理器CPU中运行。即使整个程序由多个相互独立无关的子任务组成,程序都会顺序执行。

       由于并行处理可以大幅度地提升整个任务的效率,故引入多线程编程。

       多线程中任务具有以下特点:

       (1) 这些任务的本质是异步的,需要有多个并发事务;

       (2) 各个事务的运行顺序可以是不确定的、随机的、不可预测的。

       这样的编程任务可以分成多个执行流,每个流都有一个要完成的目标。再根据不同的应用,这些子任务可能都要计算出一个中间结果,用于合并得到最后的结果。





Multithreaded的使用方法


1、Python提供了几个用于多线程编程的模块


包括thread、threading和Queue等。        

(1) thread模块: 允许程序员创建和管理线程,它提供了基本的线程和锁的支持。thread模块仅仅了解就行,你应该使用更高级别的threading等。

原因之一:thread不支持守护线程 :其中thread模块需要避免的一个原因是:它不支持守护线程。当主线程退出时,所有的子线程不论它们是否还在工作,都会被强行退出。有时我们并不期望这种行为,这就引入了守护线程的概念。Threading模块支持守护线程。  

原因之二:threading的Thread类是主要的运行对象。它有很多thread模块里没有的函数。  


(2) threading模块: 允许程序员创建和管理线程,它提供了更高级别,更强的线程管理的功能。


Thread          #表示一个线程的执行的对象

Lock            #锁原语对象(跟thread模块里的锁对象相同)

RLock           #可重入锁对象。使单线程可以再次获得已经获得了的锁(递归锁定)

Condition       #条件变量对象能让一个线程停下来,等待其他线程满足了某个“条件”。如状态的改变或值的改变

Event           #通用的条件变量。多个线程可以等待某个时间的发生,在事件发生后,所有的线程都被激活

Semaphore       #为等待锁的线程提供一个类似“等候室”的结构

BoundedSemaphore  #与Semaphore类似,只是它不允许超过初始值

Timer            #与thread类似,只是它要等待一段时间后才开始运行

(3) Queue模块: 允许用户创建一个可用于多个线程间共享数据的队列数据结构。


2、Python中使用线程有两种方式:函数或者用类来包装线程对象。


函数式:调用thread模块中的start_new_thread()函数来产生新线程。语法如下:thread.start_new_thread ( function, args[, kwargs] )

参数说明:

function - 线程函数。

args - 传递给线程函数的参数,他必须是个tuple类型。

kwargs - 可选参数。


(0.1)、没有线程的举例:使用time.sleep()函数来演示线程的工作,这个例子主要为后面线程做对比。

设计两个计时器,loop01睡眠4秒,loop02睡眠2秒,它们是在一个进程或者线程中,顺序地执行loop01()和loop02(),总运行时间为6秒。


# time.sleep()需要一个浮点型的参数,来指定“睡眠”的时间(单位秒)。这就相当于程序的运行会被挂起指定的时间。

#设计两个计时器,loop01睡眠4秒,loop02睡眠2秒,它们是在一个进程或者线程中,顺序地执行loop01()和loop02(),总运行时间为6秒。

from time import sleep, ctime  

 

def loop01():  

   print ("Start loop01 at:", ctime())  

   sleep(4)  

   print ("Loop01  done at:", ctime())  

 

def loop02():  

   print ("Start loop02 at:", ctime())  

   sleep(2)  

   print ("Loop02  done at:", ctime())  

 

def main():  

   print ("Starting at:", ctime())  

   loop01()  

   loop02()  

   print ("All done at:", ctime())  

   

if __name__ == "__main__":  

   main()  

image.png

(0.2)、使用thread模块提供简单的额多线程机制。loop01和loop02并发地被执行,显然,短的那个先结束。总的运行时间为最慢的那个线程的运行时间,而不是所有的线程的运行时间之和。 # start_new_thread()要求一定要有前两个参数,即使运行的函数不要参数,也要传一个空的元组。

    主函数中多了个sleep(6),是因为如果我们没有让主线程停下来,那主线程就会运行下一条语句,显示“All done”,然后就关闭运行着loop01和loop02的两个线程,退出了。没有写让主线程停下来等所有子线程结束后再继续运行的代码,这就是前面所说的需要同步的原因。这里采用sleep(6)作为同步机制,设置6秒=4+2秒,在主线程等待6秒后就结束了。


from time import sleep, ctime  

 

def loop01():  

   print ("Start loop01 at:", ctime())  

   sleep(4)  

   print ("Loop01  done at:", ctime())  

def loop02():  

   print ("Start loop02 at:", ctime())  

   sleep(2)  

   print ("Loop02  done at:", ctime())  

def main():  

   try:  

       print ("Starting at:", ctime())

       thread.start_new_thread(loop01, ())  

       thread.start_new_thread(loop02, ())  

       sleep(6)  

       print ("All done at:", ctime())      

   except Exception as e:

       print("Error:",e)  

   finally:      

       print("END\n")  

 

if __name__ == "__main__":  

   main()

image.png

(0.3)、线程加锁方法

     有什么好的管理线程的方法呢?而不是在主线程里做个额外的延时6秒操作。因为总的运行时间并不比单线程的代码少;而且使用sleep()函数做线程的同步操作是不可靠的;如果循环的执行时间不能事先确定的话,这可能会造成主线程过早或过晚的退出。

       这就需要引入锁的概念。下面代码执行loop函数,与前面代码的区别是不用为线程什么时候结束再做额外的等待了。使用锁之后,可以在两个线程都退出后,马上退出。


from time import sleep, ctime  

 

loops = [2,4]  #等待时间2、4秒。

def loop(nloop, nsec, lock):  #锁序号,等待时间,锁对象  

   #每个线程都会被分配一个事先已经获得的锁,在sleep()的时间到了之后就释放相应的锁以通知住线程,这个线程已经结束了。

   print("start loop", nloop, "at:", ctime())  

   sleep(nsec)

   print("loop ", nloop, "done at:", ctime())  

   lock.release()  #解锁。线程结束时需做解锁操作,调用lock.release()函数;  

 

def main():  

   print("starting at:", ctime() )

   locks =[]  

   nloops = range(len(loops))          #以loops数组创建列表并赋值给nloops  

         

   #第一个for循环创建锁:thread.allocate_lock()

   for i in nloops:  

       lock = thread.allocate_lock()   #创建锁对象,后边添加到locks列表内  

       lock.acquire()                  #获取锁对象,加锁。即分别调用各个锁的acquire()函数获得锁对象。获得锁表示“把锁锁上”,并放到锁列表locks中;

       locks.append(lock)              #追加到locks[]数组中  

       

   #第二个for循环创建线程:thread.start_new_thread(对应线程循环号,睡眠时间,锁)

   for i in nloops:  

       thread.start_new_thread(loop,(i,loops[i],locks[i]))  #执行多线程 (自定义的loop函数,函数参数)  

       

   #最后这个循环是一直等待(达到暂停主线程的目的):直到两个锁都被解锁才继续运行。它是顺序检查每个锁,主线程需不停地对所有锁进行检查直到都释放。

   for i in nloops:   #循环等待顺序检查每个所都被解锁才停止

       while locks[i].locked():  

           pass

   print("all end:", ctime() )  

 

if __name__ == "__main__":  

   main()  

image.png



版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
26个例子来搞懂数据库锁
  1 前言   数据库大并发操作要考虑死锁和锁的性能问题。看到网上大多语焉不详(尤其更新锁),所以这里做个简明解释,为下面描述方便,这里用T1代表一个数据库执行请求,T2代表另一个请求,也可以理解为T1为一个线程,T2 为另一个线程。T3,T4以此类推。下面以SQL Server(2005)为例。   2 锁的种类共享锁(Shared lock)。   例1:   ----------------------------------------   T1: select * from table (请想象它需要执行1个小时之久,后面的sql语句请都这么想象)   T2: upda
7 0
3年工作经验的java程序员应该具备的技能
  1、基本语法   这包括static、final、transient等关键字的作用,foreach循环的原理等等。今天面试我问你static关键字有哪些作 用,如果你答出static修饰变量、修饰方法我会认为你合格,答出静态块,我会认为你不错,答出静态内部类我会认为你很好,答出静态导包我会对你很满 意,因为能看出你非常热衷研究技术。   最深入的一次,我记得面试官直接问到了我Volatile关 键字的底层实现原理(顺便插一句,面试和被面试本身就是相对的,面试官能问这个问题同时也让面试者感觉到面试官也是一个喜爱研究技术的人,增加了面试者对 公司的好感,我最终选择的就是问了这个问题的公司)
5 0
Java 性能优化:35个小细节,让你提升Java代码运行的效率
  代码优化,一个很重要的课题。可能有些人觉得没用,一些细小的地方有什么好修改的,改与不改对于代码的运行效率有什么影响呢?这个问题我是这么考虑的,就像大海里面的鲸鱼一样,它吃一条小虾米有用吗?没用,但是,吃的小虾米一多之后,鲸鱼就被喂饱了。   代码优化也是一样,如果项目着眼于尽快无BUG上线,那么此时可以抓大放小,代码的细节可以不精打细磨;但是如果有足够的时间开发、维护代码,这时候就必须考虑每个可以优化的细节了,一个一个细小的优化点累积起来,对于代码的运行效率绝对是有提升的。
12 0
SAP成都研究院非典型程序猿,菜园子小哥:当我用UI5诊断工具时我用些什么
SAP成都研究院非典型程序猿,菜园子小哥:当我用UI5诊断工具时我用些什么
4 0
程序中的Bug是如何产生的?
  ★   Bug,总是令人讨厌的东西。那Bug是如何产生的呢?作为高级软件架构师和软件测试工程师的易哥将在这篇文章中解答这个问题。   ”   说起Bug,大家都认为它是被“写”出来的,即主要在开发阶段产生。   但其实Bug的产生最有可能是在需求阶段(意外吧!这是有统计数据证明的),且在需求阶段产生的Bug影响最大。当然,在设计、开发、使用阶段也会出现Bug。   接下来我们详细了解下Bug的相关知识。
5 0
重磅发布开源框架2.0RC版 、生物计算平台「螺旋桨」,百度飞桨交了份年终成绩单
在 12 月 20 日举行的「WAVE SUMMIT+ 2020 深度学习开发者峰会」上,飞桨平台交出了一份非常亮眼的年终成绩单。
5 0
我与ECS
作为一名计算机专业的学生!服务器是基本刚需!学习docker!学习redis!学习运维!学习云部署!学习ngix!我只能说!ECS是真的香!
9 0
金庸和古龙,Netweaver和微服务,以及SAP Hybris Revenue Cloud
金庸和古龙,Netweaver和微服务,以及SAP Hybris Revenue Cloud
7 0
53个Python库,你必须要试试
  Python库大全   大邓将Python库整理为8部分,对每个库稍加自己的理解和评价,对Python感兴趣的同学可以收藏起来   ? 网络爬虫 ? 数据库 ? 数据分析 ? 机器学习 ? 可视化 ? 文本分析 ? GUI窗体软件开发 ? 自动化办公   私信小编01即可获取大量Python学习资料   网络爬虫
8 0
Django model字段类型解析
  Model字段认识   V=models.CharField(max_length=None<, **options>)    #varchar   V=models.EmailField()    #varchar   V=models.URLField()    #varchar   V=models.FileField(upload_to=None<, max_length=100, **options>)    #varchar #upload_to指定保存目录可带格式,   V=models.ImageField(upload_to=None<, height_fie
6 0
+关注
一个处女座的程序猿
国内互联网圈知名博主、人工智能领域优秀创作者,全球最大中文IT社区博客专家、CSDN开发者联盟生态成员、中国开源社区专家、华为云社区专家、51CTO社区专家、Python社区专家等,曾受邀采访和评审十多次。仅在国内的CSDN平台,博客文章浏览量超过2500万,拥有超过57万的粉丝。
1701
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
文娱运维技术
立即下载
《SaaS模式云原生数据仓库应用场景实践》
立即下载
《看见新力量:二》电子书
立即下载