资料来源:https://www.cnblogs.com/alex3714/articles/5230609.html

http://python.jobbole.com/86406/

https://www.cnblogs.com/wupeiqi/articles/5040827.html

https://www.cnblogs.com/tkqasn/p/5700281.html

在此感谢前辈们的指导,以下均为纯手打

一,程序,进程,线程概念的区分

在计算机中,程序不能单独运行,必须把它分配到内存中,系统为它分配资源才能运行,而这种执行的程序就称之为进程。

一句话总结:程序是指令的集合,它是程序的静态描述文本;进程确是程序的一次执行活动,是动态概念

那么,线程是什么呢,它与进程有什么关系呢,我们看几个例子:

线程是执行上下文,它是CPU执行指令流所需的全部信息。

1,假设你正在读一本书,现在你想休息一下,但你希望能够回来,从你停下来的确切地点恢复阅读。一个实现的方法就是记下页码,行数和字数。所以你阅读书的执行上下文是这3个数字。

2,如果你有一个室友,而且她使用相同的技巧,她可以在你不使用的时候拿书,然后从她停止阅读的地方继续阅读。然后你可以收回它,从你原来的地方恢复它。

因此执行上下文(因此线程)由CPU寄存器的值组成。

综上:线程不同于进程。线程是执行的上下文,而进程是与计算相关联的一堆资源。一个进程可以有一个或多个线程(资源当中肯定不止一个上下文)。

关于进程与线程还有几个重要的区别

1,与进程相关的资源包括内存页(进程中的所有线程对内存具有相同的视图)、文件描述符(例如,打开套接字)和安全凭据(例如启动进程的用户ID)。

2,很容易创建新线程;新进程需要重复父进程。

3,线程可以对相同进程的线程进行相当的控制;进程只能对子进程进行控制。

4,对主线程的更改(取消、优先级更改等)可能影响进程的其他线程的行为;对父进程的更改不会影响子进程。

二:有了进程,为什么需要线程(拓展)

1,进程只能在一个时间内干一件事,但如果想同时干两件或多件事,进程就无能为力了。

2,进程在执行过程中,如果发生阻塞,那么进程就会挂起,无法执行程序。

三,道路从线程开始:

threading 模块提供的常用方法: 
  threading.currentThread(): 返回当前的线程变量。 
  threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。 
  threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。

threading 模块提供的常量:

  threading.TIMEOUT_MAX 设置threading全局超时时间。

线程的方法:

  • start            线程准备就绪,等待CPU调度

  • setName      为线程设置名称

  • getName      获取线程名称

  • setDaemon   设置为后台线程或前台线程(默认)
                       如果是后台线程,主线程执行过程中,后台线程也在进行,主线程执行完毕后,后台线程不论成功与否,均停止
                        如果是前台线程,主线程执行过程中,前台线程也在进行,主线程执行完毕后,等待前台线程也执行完成后,程序停止

  • join              逐个执行每个线程,执行完毕后继续往下执行,该方法使得多线程变得无意义

  • run              线程被cpu调度后自动执行线程对象的run方法

接下来,围绕这几个方法,和线程的两种调用方式进行讲解

1,直接调用:

import threading
import time
def sayhi(num):
   print("running on number:%s"%num)
   time.sleep(3)
if __name__=='__main__':
   t1=threading.Thread(target=sayhi,args=(1,))#线程实例,逗号必须加
   t2 = threading.Thread(target=sayhi, args=(2,))  # 线程实例
   t1.start()#启动线程
   t2.start()#启动另一个线程
   print(t1.getName())

print(t2.getName())

# 构造方法:
# Thread(group=None, target=None, name=None, args=(), kwargs={})

#   group: 线程组,目前还没有实现,库引用中提示必须是None;
#   target: 要执行的方法;
#   name: 线程名;

#   args / kwargs: 要传入方法的参数。

再来看一个间接调用(继承式调用):

import threading
import time
class MyThread(threading.Thread):
   def __init__(self, num):
       threading.Thread.__init__(self)
       self.num = num
   def run(self):  # 定义每个线程要运行的函数

print("running on number:%s" % self.num)

time.sleep(3)
if __name__ == '__main__':
   t1 = MyThread(1)
   t2 = MyThread(2)
   t1.start()
   t2.start()

接下来对这两个调用进行深究

一下子开五十个进程,看看时间多少

import threading
import time
def run(n):
   print("task",n)
   time.sleep(2)
   print("task done", n)
start_time=time.time()
for i in range(50):
   t1=threading.Thread(target=run,args=("t-%s"%i,))
   t1.start()

print("cost:",time.time()-start_time)

观察它所消耗的时间是

摘取部分结果

task t-46

task t-47

task t-48

task t-49

------------all threads has finished

cost: 0.0070035457611083984

task done t-2

task done t-1

task done t-0

task done t-8

为什么会出现这种情况呢,是因为主线程与子线程无关,不能在主线程中测子线程,因为主线程会执行自己的,,这里可能说的有点抽象,emmmmn,其实我也是在把这遍代码,码完后,才有些懂得。

主线程可以看成是:

import threading
import time
def run(n):
   print("task",n

start_time=time.time()
for i in range(50):
   t1=threading.Thread(target=run,args=("t-%s"%i,))
   t1.start()

print("cost:",time.time()-start_time)

这段代码集合(把run代码中的sleep去掉了),他只会执行自己的,不会等子线程的结束,所以将task打完后就把时间打印出来

那么如何让主线程等待呢:

import threading
import time
def run(n):
   print("task",n)
   time.sleep(2)
   print("task done",threading.current_thread())#返回到当前线程的对象
start_time=time.time()
t_objs=[]
for i in range(50):
   t1=threading.Thread(target=run,args=("t-%s"%i,))
   t1.start()
   t_objs.append(t1)
for t in t_objs:
   t.join()#结束
print("------------all threads has finished",threading.current_thread())

print("cost:",time.time()-start_time)#主线程与子线程无关,所以不能在主线程中测子线程

部分结果展示:

task done <Thread(Thread-34, started 3940)>

task done <Thread(Thread-33, started 6752)>

task done <Thread(Thread-44, started 10280)>

task done <Thread(Thread-43, started 14988)>

task done <Thread(Thread-42, started 7344)>

task done <Thread(Thread-41, started 8984)>

task done <Thread(Thread-40, started 9972)>

task done <Thread(Thread-48, started 12672)>

task done <Thread(Thread-47, started 9012)>

task done <Thread(Thread-46, started 704)>

task done <Thread(Thread-45, started 7840)>

task done <Thread(Thread-49, started 10924)>

task done <Thread(Thread-50, started 14836)>

------------all threads has finished <_MainThread(MainThread, started 10780)>

cost: 2.0096378326416016

此时可以在主线程中测子线程了

注意,为什么多线程会称之为多线程,因为会用join方法,有的哥们儿这样写

import threading
import time
def run(n):
   print("task",n)
   time.sleep(2)
   print("task done",threading.current_thread())#返回到当前线程的对象
start_time=time.time()
t_objs=[]
for i in range(50):
   t1=threading.Thread(target=run,args=("t-%s"%i,))
   t1.start()
   t_objs.append(t1)
   t1.join()#结束
print("------------all threads has finished",threading.current_thread())
print("cost:",time.time()-start_time)#主线程与子线程无关,所以不能在主线程中测子线程

他就变成了顺序的线程了,造成线程的阻塞了,应该竭力避免。

好了,我们再来看看守护(后台)线程的实例:

setDaemon   设置为后台线程或前台线程(默认)
                   如果是后台线程,主线程执行过程中,后台线程也在进行,主线程执行完毕后,后台线程不论成功与否,均停止

如果是前台线程,主线程执行过程中,前台线程也在进行,主线程执行完毕后,等待前台线程也执行完成后,程序停止

import threading
import time
def run(n):
   print("task",n)
   time.sleep(2)
   print("task done",threading.current_thread())#返回到当前线程的对象
start_time=time.time()
t_objs=[]
for i in range(50):
   t1=threading.Thread(target=run,args=("t-%s"%i,))
   t1.setDaemon(True)  # 把线程转变成守护线程,不管分线程,主进程结束了,直接结束,不等分线程
   t1.start()
   t_objs.append(t1)

# for t in t_objs:
#     t.join()#结束
print("------------all threads has finished",threading.current_thread())
print("cost:",time.time()-start_time)#主线程与子线程无关,所以不能在主线程中测子线程

这个案例比前面几个案例修改了什么了。没错,当主线程结束的时候,守护线程也会跟着结束

t1.setDaemon(True)  # 把线程转变成守护线程,不管分线程,主进程结束了,直接结束,不等分线程t1.start()

这个是固定格式,改变位置容易出错

以上是稍微简单的些的概念,接下来看第二部分

线程锁的引出:

一个进程可以启动多个进程,多个线程共享父进程的内存空间,也就意味着每个线程可以访问一份数据,此时,如果有两个线程要修改同一份数据,会出现什么状况?

会导致数据出错,这里,我摘取前辈们的一部分解释,因为相比于2.7版本,3中做了许多改进,前辈们的问题已经不会出现,目测是python源代码中加了内置锁。

import time
import threading
def addNum():
    global num #在每个线程中都获取这个全局变量
    print('--get num:',num )
    time.sleep(1)
    num  -=1 #对此公共变量进行-1操作
num = 100  #设定一个共享变量
thread_list = []
for in range(100):
    = threading.Thread(target=addNum)
    t.start()
    thread_list.append(t)
for 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。那怎么办呢? 很简单,每个线程在要修改公共数据时,为了避免自己在还没改完的时候别人也来修改此数据,可以给这个数据加一把锁, 这样其它线程想修改此数据时就必须等待你修改完毕并把锁释放掉后才能再访问此数据。

上面是我复制粘贴,原话,这里就不作解释了

我们来看一下怎样避免前辈们的情况

import time
import threading
def addNum():
   global num#在每个线程中都获得这个全局变量
   print('--get num:',num)
   time.sleep(1)
   lock.acquire()#修改数据前加锁
   num+=1
   lock.release()  # 修改后释放
   print('--get num', num)
num=0
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()#调用run1() run1()是一个方法
   print('--------between run1 and run2-----')
   res2 = run2()#执行run2
   lock.release()
   print(res, res2)
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)

信号量

所锁只能一个对数据进行更改,但信号量可以多个

import threading,time
def run(n):
   semaphore.acquire()
   time.sleep(1)
   global num
   num+=1
   print("run the thread:%s\n"%n)
   print(num)
   semaphore.release()
if __name__=='__main__':

num=0
   semaphore=threading.BoundedSemaphore(5)#最多允许5个线程同时进行
   for i in range(22):
       t=threading.Thread(target=run,args=(i,))
       t.start()
while threading.active_count()!=1:
   pass
else:

print('---all threads done----')

以下分享三个案例

先来一个简单的实例

import threading
import time

class MyThread(threading.Thread):
   def __init__(self, signal):
       threading.Thread.__init__(self)
       self.singal = signal

def run(self):
       print("I am %s,I will sleep ..." % self.name)
       self.singal.wait()
       print("I am %s, I awake..." % self.name)

if __name__ == "__main__":
   singal = threading.Event()
   for t in range(0, 3):
       thread = MyThread(singal)
       thread.start()

print("main thread sleep 3 seconds... ")
   time.sleep(3)

singal.set()

关于红绿灯的

import time
import threading
event=threading.Event()
def lighter():
   count=0
   event.set()  # 变绿灯
   while True:
       if count>5 and count <10:#这一步是改成红灯
           event.clear()
           print("\033[41;1mred light is on....\033[0m")
       elif count>10:
           event.set()#变绿灯
           count=0
       else:
           print("\033[42;1mgreen light is on....\033[0m")
       time.sleep(1)
       count+=1
def car(name):
   while True:
       if event.is_set(): #代表绿灯
           time.sleep(0.1)
           print("[%s] running..."%name)

else:
           print("[%s] sees red light , waiting...." %name)
           event.wait()
           print("\033[34;1m[%s] green light is on, start going...\033[0m" %name)

light = threading.Thread(target=lighter,)
light.start()
car1 = threading.Thread(target=car,args=("Tesla",))
car1.start()

#此处还有一个案例没完成,一个线程与多个线程之间的通信

未完待续,最近几天会推出线程与爬虫结合的实例与模板

本文转自眉间雪 51CTO博客,原文链接:http://blog.51cto.com/13348847/1983306,如需转载请自行联系原作者

python线程,进程,协程相关推荐

  1. python线程进程协程面试_Python学习经验之谈:关于协程的理解和其相关面试问题...

    都知道Python非常适合初学者学习来入门编程,昨天有伙伴留言说面试了Python岗位,问及了一个关于协程的问题,想了想还是跟大家出一篇协程相关的文章和在Python面试中可能会问及的相关面试问题.都 ...

  2. 打开线程 | 进程 | 协程的大门

    不知从几何起,可能是大三那年的操作系统考试,也可能是刚经历完的秋招,这些概念总是迷迷糊糊,可能自己回答的和其他人的答复也差不多,并没有什么亮点,通常都会以:「我们换个题」的方式结束,有时候也挺尴尬的. ...

  3. python协程和线程_python之并发编程(线程\进程\协程)

    一.进程和线程 1.进程 假如有两个程序A和B,程序A在执行到一半的过程中,需要读取大量的数据输入(I/O操作),而此时CPU只能静静地等待任务A读取完数据才能继续执行,这样就白白浪费了CPU资源.是 ...

  4. python apply_async死锁_python之并发编程(线程\进程\协程)

    一.进程和线程 1.进程假如有两个程序A和B,程序A在执行到一半的过程中,需要读取大量的数据输入(I/O操作),而此时CPU只能静静地等待任务A读取完数据才能继续执行,这样就白白浪费了CPU资源.是不 ...

  5. python_21_线程+进程+协程

    python_线程_进程_协程 什么是线程? -- os能够进行运算调度的最小单位,被包含在进程之中,是一串指令的集合 -- 每个线程都是独立的,可以访问同一进程下所有的资源 什么是进程? -- 每个 ...

  6. 十四丶并发编程(线程 进程 协程)

    Yuan先生 知识预览 操作系统 回到顶部 操作系统 一 为什么要有操作系统? 现代计算机系统是由一个或者多个处理器,主存,磁盘,打印机,键盘,鼠标显示器,网络接口以及各种其他输入输出设备组成的复杂系 ...

  7. python 协程、进程、线程_Python的进程、线程和协程 · Donzy’s Blogs

    0.前言 在计算机技术领域,吞吐量(throughput)是计算机在指定的一段时间内完成编程技术如何影响.本文主要讨论Python的多进程.多线程及协程等编程技术在不同场景下对系统吞吐量的影响. 1. ...

  8. python 协程可以嵌套协程吗_Python线程、协程探究(2)——揭开协程的神秘面纱...

    一.上集回顾 在上一篇中我们主要研究了python的多线程困境,发现多核情况下由于GIL的存在,python的多线程程序无法发挥多线程该有的并行威力.在文章的结尾,我们提出如下需求: 既然python ...

  9. python 多线程和协程结合_一文讲透 “进程、线程、协程”

    本文从操作系统原理出发结合代码实践讲解了以下内容: 什么是进程,线程和协程? 它们之间的关系是什么? 为什么说Python中的多线程是伪多线程? 不同的应用场景该如何选择技术方案? ... 什么是进程 ...

  10. 怎么更进一步学python_【百尺竿头,更进一步学Python】Python进阶课程——进程,线程和协程的区别...

    本文带来各类奇怪的IT百科知识. [百尺竿头,更进一步学Python]Python进阶课程--进程:线程和协程的区别 现在多进程多线程已经是老生常谈了:协程也在最近几年流行起来.今天我们本文主要介绍进 ...

最新文章

  1. Linux下获取详细硬件信息的工具:Dmidecode命令详解
  2. 保监会:《保险公司信息系统安全管理指引(试行)》
  3. 【转】 ASP.NET 3.5中使用新的ListView控件
  4. arc.archives.class.php关于分页错位怎么修改,解决织梦分页错位的办法
  5. 多线程并发之原子性(六)
  6. Linux的I2C 设备驱动 -- mini2440 上i2c接口触摸屏驱动
  7. MongoDB副本集成员状态
  8. AutoCAD 2010建筑土木制图高清实例视频教程
  9. TF2.0-tf.keras.callbacks.EarlyStopping
  10. 实现平衡二叉排序树的各种算法(包括二叉树的递归遍历、非递归遍历)
  11. unittest 测试
  12. IBM行贿案凸显外企在华的非正常之道
  13. 知也atitit.解决struts2 SpringObjectFactory.getClassInstance NullPointerException  v2 q31无涯 - I
  14. 关于一些java命令作用
  15. 六子冲棋,六子炮棋,二打一棋,箭棋,炮棋(java单机版)java人机对战
  16. android 圆形自定义进度条,Android实现自定义圆形进度条
  17. java贪吃蛇项目总结_贪吃蛇总结
  18. 云盘+Git GUI实现云盘文件版本控制
  19. 2021年度训练联盟热身训练赛第八场 自我总结
  20. Private VLAN 与Switchport Protected

热门文章

  1. 给定数组 求和等于固定值 算法_[见题拆题] 大厂面试算法真题解析 - 第一期开张...
  2. python中使用函数的优点_Python基础之函数基本用法与进阶详解
  3. 判断用户是否存在再进行新增_4招教你判断抖音真假粉,快速分辨抖音号的真实度!...
  4. Android安全加密:非对称加密
  5. red hat linux FTP配置
  6. lr mysql 增删改查_Python对MySQL进行增删查改
  7. angularjs组件间通讯_详解Angular2组件之间如何通信
  8. 怎么用python自制计算公式_自制计算经纬度位移 python 程序
  9. docker 如何加入kubernetes_使用 Kind 在 5 分钟内快速部署一个 Kubernetes 高可用集群...
  10. mysql查询filter_子查询包含or引起的filter性能问题案例