python线程,进程,协程

简介:

资料来源: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,如需转载请自行联系原作者





相关文章
|
8天前
|
安全 Java 数据处理
Python网络编程基础(Socket编程)多线程/多进程服务器编程
【4月更文挑战第11天】在网络编程中,随着客户端数量的增加,服务器的处理能力成为了一个重要的考量因素。为了处理多个客户端的并发请求,我们通常需要采用多线程或多进程的方式。在本章中,我们将探讨多线程/多进程服务器编程的概念,并通过一个多线程服务器的示例来演示其实现。
|
18天前
|
算法 数据处理 Python
Python并发编程:解密异步IO与多线程
本文将深入探讨Python中的并发编程技术,重点介绍异步IO和多线程两种常见的并发模型。通过对比它们的特点、适用场景和实现方式,帮助读者更好地理解并发编程的核心概念,并掌握在不同场景下选择合适的并发模型的方法。
|
1天前
|
调度 Python
Python多线程、多进程与协程面试题解析
【4月更文挑战第14天】Python并发编程涉及多线程、多进程和协程。面试中,对这些概念的理解和应用是评估候选人的重要标准。本文介绍了它们的基础知识、常见问题和应对策略。多线程在同一进程中并发执行,多进程通过进程间通信实现并发,协程则使用`asyncio`进行轻量级线程控制。面试常遇到的问题包括并发并行混淆、GIL影响多线程性能、进程间通信不当和协程异步IO理解不清。要掌握并发模型,需明确其适用场景,理解GIL、进程间通信和协程调度机制。
16 0
|
17天前
|
数据采集 Java API
python并发编程: Python使用线程池在Web服务中实现加速
python并发编程: Python使用线程池在Web服务中实现加速
17 3
python并发编程: Python使用线程池在Web服务中实现加速
|
19天前
|
Java 测试技术 Python
Python开启线程和线程池的方法
Python开启线程和线程池的方法
14 0
Python开启线程和线程池的方法
|
23天前
|
API 数据处理 调度
Python中的异步编程与协程应用
传统的Python编程在处理IO密集型任务时常常面临效率低下的问题,而异步编程和协程技术的引入为解决这一问题提供了有效的途径。本文将介绍Python中异步编程的基本概念,深入探讨asyncio库的使用以及协程在实际项目中的应用,旨在帮助开发者更好地理解和运用异步编程技术。
|
1月前
|
并行计算 调度 开发者
深入浅出Python协程:提升你的异步编程效率
在当今快速发展的软件开发领域,异步编程已成为提高程序性能和用户体验的关键技术。Python,作为一门广泛使用的高级编程语言,其协程(Coroutine)功能为开发者提供了强大的异步编程工具。本文将从协程的基本概念入手,通过实例深入浅出地讲解如何在Python中有效利用协程来提升异步编程的效率和可读性。我们将探讨协程的工作原理、与传统多线程/多进程相比的优势,以及如何在实际项目中应用协程来解决复杂的并发问题。通过本文的学习,读者将能够掌握Python协程的核心知识,为构建高效、可维护的异步应用奠定坚实基础。
|
1月前
|
调度 Python
python协程—asyncio模块
python协程—asyncio模块
20 0
|
1月前
|
API 开发者 Python
深入浅出Python协程:提升并发编程效率
在当今高速发展的互联网时代,高并发成为了软件开发中的一个重要需求。本文将引领读者深入理解Python中的协程(Coroutine)概念,探讨其在并发编程中的应用及优势。我们将从协程的基础概念出发,通过实例讲解如何使用asyncio库来编写高效的异步代码。文章旨在帮助读者掌握协程的工作原理和使用方法,从而在实际开发中能够更好地利用Python进行高效的并发编程。
|
1月前
|
数据采集 调度 开发者
深入浅出Python协程:提升并发编程效率
本文旨在为读者揭开Python协程的神秘面纱,通过深入浅出的方式阐述其工作原理及应用场景。不同于传统的技术文章摘要,我们将以一种独特的视角,将协程比作一场精心编排的交响乐,其中每一个乐章都是一个独立的任务,共同演绎出并发编程的华丽篇章。文章将从协程的基本概念切入,通过对比线程和进程,逐步深入到事件循环、异步IO等核心机制,最后通过案例分析,让读者能够掌握使用Python协程处理高并发任务的技巧,从而提升编程效率。