前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >python进程和线程

python进程和线程

原创
作者头像
织幻妖
修改2021-03-04 17:58:31
修改2021-03-04 17:58:31
56700
代码可运行
举报
运行总次数:0
代码可运行

进程

进程是应用程序的执行实例,每一个运行中的程序就是一个进程

线程

线程是进程的组成部分,一个进程可以拥有多个线程

当一个进程里只有一个线程时,叫作单线程。超过一个线程就叫作多线程。

创建线程

Python 主要通过两种方式来创建线程:

1.调用Thread类的构造器创建线程

使用 threading 模块中 Thread 类的构造器创建线程。即直接对类 threading.Thread 进行实例化创建线程,并调用实例化对象的 start() 方法启动线程。

a.单线程

代码演示:

代码语言:javascript
代码运行次数:0
复制
import threading
#定义线程要调用的方法,*add可接收多个以非关键字方式传入的参数
def action(*add):
    for arc in add:
        #调用 getName() 方法获取当前执行该程序的线程名
        print(threading.current_thread().getName() +" "+ arc)
#定义为线程方法传入的参数
my_tuple = ("python1",\
            "python2",\
            "python3")
#创建线程
thread = threading.Thread(target = action,args =my_tuple)
#启动线程
thread.start()

执行效果:

代码语言:javascript
代码运行次数:0
复制
Thread-1 python1
Thread-1 python2
Thread-1 python3

b.多线程

代码演示:

代码语言:javascript
代码运行次数:0
复制
import threading
#定义线程要调用的方法,*add可接收多个以非关键字方式传入的参数
def action(*add):
    for arc in add:
        #调用 getName() 方法获取当前执行该程序的线程名
        print(threading.current_thread().getName() +" "+ arc)
#定义为线程方法传入的参数
my_tuple = ("python1",\
            "python2",\
            "python3")
#创建线程
thread = threading.Thread(target = action,args =my_tuple)
#启动线程
thread.start()

for i in range(5):
    print(threading.current_thread().getName())

执行结果:

代码语言:javascript
代码运行次数:0
复制
Thread-1 python1MainThread

MainThread
MainThread
MainThread
Thread-1 python2MainThread

Thread-1 python3

我们可以看到,当有 2 个线程时,分别为主线程 MainThread 和子线程 Thread-1,它们以并发方式执行,即 Thread-1 执行一段时间,然后 MainThread 执行一段时间。通过轮流获得 CPU 执行一段时间的方式,程序的执行在多个线程之间切换,从而给用户一种错觉,即多个线程似乎同时在执行。

2.继承Thread类创建线程类

继承 threading 模块中的 Thread 类创建线程类。即用 threading.Thread 派生出一个新的子类,将新建类实例化创建线程,并调用其 start() 方法启动线程。

通过继承 Thread 类,我们可以自定义一个线程类,从而实例化该类对象,获得子线程。

需要注意的是,在创建 Thread 类的子类时,必须重写从父类继承得到的 run() 方法。因为该方法即为要创建的子线程执行的方法,其功能如同第一种创建方法中的 action() 自定义函数。

代码演示:

代码语言:javascript
代码运行次数:0
复制
import threading

#创建子线程类,继承自 Thread 类
class my_Thread(threading.Thread):
    def __init__(self,add):
        threading.Thread.__init__(self)
        self.add = add
    # 重写run()方法
    def run(self):
         for arc in self.add:
            #调用 getName() 方法获取当前执行该程序的线程名
            print(threading.current_thread().getName() +" "+ arc)

#定义为 run() 方法传入的参数
my_tuple = ("python1",\
            "python2",\
            "python3")
#创建子线程
mythread = my_Thread(my_tuple)
#启动子线程
mythread.start()
#主线程执行此循环
for i in range(5):
    print(threading.current_thread().getName())

执行结果:

代码语言:javascript
代码运行次数:0
复制
Thread-1 python1MainThread
MainThread

Thread-1 python2
MainThreadThread-1 python3

MainThread
MainThread

线程的生命周期

线程从创建到消亡会历经 5 种状态,分别是新建、就绪、运行、阻塞和死亡

新建:新创建的线程在调用 start() 方法之前,不会得到执行,此阶段的线程就处于新建状态。

就绪:当位于新建状态的线程调用 start() 方法后,该线程就转换到就绪状态。

运行:当位于就绪状态的线程得到了 CPU,并开始执行 target 参数执行的目标函数或者 run() 方法,就表明当前线程处于运行状 态。

阻塞:如果当前有多个线程处于就绪状态(等待 CPU 调度)时,处于运行状态的线程将无法一直霸占 CPU 资源,为了使其它线 程也有执行的机会,CPU 会在一定时间内强制当前运行的线程让出 CPU 资源,以供其他线程使用。而对于获得 CPU 调度 却没有执行完毕的线程,就会进入阻塞状态。

死亡:对于获得 CPU 调度却未执行完毕的线程,它会转入阻塞状态,待条件成熟之后继续转入就绪状态,重复争取 CPU 资源, 直到其执行结束。执行结束的线程将处于死亡状态。

守护线程

Python支持创建另一种线程,称为守护线程(或后台线程)。此类线程的特点是,当程序中主线程及所有非守护线程执行结束时,未执行完毕的守护线程也会随之消亡(进行死亡状态),程序将结束运行。

代码演示:

代码语言:javascript
代码运行次数:0
复制
import threading
#定义线程要调用的方法,*add可接收多个以非关键字方式传入的参数
def action(length):
    for arc in range(length):
        #调用 getName() 方法获取当前执行该程序的线程名
        print(threading.current_thread().getName()+" "+str(arc))
#创建线程
thread = threading.Thread(target = action,args =(20,))
#将thread设置为守护线程
thread.daemon = True
#启动线程
thread.start()
#主线程执行如下语句
for i in range(5):
    print(threading.current_thread().getName())

执行结果:

代码语言:javascript
代码运行次数:0
复制
Thread-1 0MainThread
Thread-1 1
Thread-1 2

MainThread
Thread-1 3
Thread-1 4
Thread-1 5
Thread-1 6
Thread-1 7
Thread-1 8
Thread-1 9
MainThread
MainThread
Thread-1 10MainThread

死锁

当两个线程相互等待对方释放资源时,就会发生死锁。

一旦出现死锁,整个程序既不会发生任何异常,也不会给出任何提示,只是所有线程都处于阻塞状态,无法继续。

代码演示:

代码语言:javascript
代码运行次数:0
复制
import threading
import time
class A:
    def __init__(self):
        self.lock = threading.RLock()
    def foo(self, b):
        try:
            self.lock.acquire()
            print("当前线程名: " + threading.current_thread().name\
                + " 进入了A实例的foo()方法" )    
            time.sleep(0.2)
            print("当前线程名: " + threading.current_thread().name\
                + " 企图调用B实例的last()方法") 
            b.last()
        finally:
            self.lock.release()
    def last(self):
        try:
            self.lock.acquire()
            print("进入了A类的last()方法内部")
        finally:
            self.lock.release()
class B:
    def __init__(self):
        self.lock = threading.RLock()
    def bar(self, a):
        try:
            self.lock.acquire()
            print("当前线程名: " + threading.current_thread().name\
                + " 进入了B实例的bar()方法" )   
            time.sleep(0.2)
            print("当前线程名: " + threading.current_thread().name\
                + " 企图调用A实例的last()方法")  
            a.last()
        finally:
            self.lock.release()
    def last(self):
        try:
            self.lock.acquire()
            print("进入了B类的last()方法内部")
        finally:
            self.lock.release()
a = A()
b = B()
def init():
    threading.current_thread().name = "主线程"
    # 调用a对象的foo()方法
    a.foo(b)
    print("进入了主线程之后")
def action():
    threading.current_thread().name = "副线程"
    # 调用b对象的bar()方法
    b.bar(a)
    print("进入了副线程之后")
# 以action为target启动新线程
threading.Thread(target=action).start()
# 调用init()函数
init()

执行效果:

死锁了

程序既无法向下执行,也不会抛出任何异常,就一直“僵持”着。究其原因,是因为上面程序中 A 对象和 B 对象的方法都是线程安全的方法。

程序中有两个线程执行,副线程的线程执行体是 action() 函数,主线程的线程执行体是 init() 函数(主程序调用了 init() 函数)。其中在 action() 函数中让 B 对象调用 bar() 方法,而在 init() 函数中让 A 对象调用 foo() 方法。

死锁是不应该在程序中出现的,在编写程序时应该尽量避免出现死锁。下面有几种常见的方式用来解决死锁问题:

  1. 避免多次锁定。尽量避免同一个线程对多个 Lock 进行锁定。
  2. 具有相同的加锁顺序。如果多个线程需要对多个 Lock 进行锁定,则应该保证它们以相同的顺序请求加锁。
  3. 使用定时锁。程序在调用 acquire() 方法加锁时可指定 timeout 参数,该参数指定超过 timeout 秒后会自动释放对 Lock 的锁定,这样就可以解开死锁了。
  4. 死锁检测。死锁检测是一种依靠算法机制来实现的死锁预防机制,它主要是针对那些不可能实现按序加锁,也不能使用定时锁的场景的。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 进程
    • 进程是应用程序的执行实例,每一个运行中的程序就是一个进程
  • 线程
    • 线程是进程的组成部分,一个进程可以拥有多个线程
    • 当一个进程里只有一个线程时,叫作单线程。超过一个线程就叫作多线程。
  • 创建线程
    • Python 主要通过两种方式来创建线程:
    • 1.调用Thread类的构造器创建线程
      • a.单线程
      • b.多线程
    • 2.继承Thread类创建线程类
  • 线程的生命周期
  • 守护线程
  • 死锁
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档