09-1 | 并发编程基本概念

逸兴
逸兴
逸兴
57
文章
25
评论
2020-06-0118:52:23
评论
1 2179字阅读7分15秒

并发编程基本概念

并发和并行的区别

并发:一段时间内,必须做完一些事情

并行:同一时刻,同时做几件事

并行是并发的一种水平扩展的解决手段。

进程和线程

进程是系统中最小的资源管理单位,是程序运行在内存中的实例。

线程是系统中最小的运算调度单位,包含在进程中,是实际干活的。

线程,有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元。
一个标准的线程由线程ID,当前指令指针(PC)、寄存器集合和堆、栈组成。
在许多系统中,创建一个线程比创建一个进程快10-100倍。(并不代表线程速度快于进程)

线程基本概念

线程状态

状态含义
就绪(Ready)线程能够运行,但在等待被调度。可能线程刚刚创建启动,或刚刚从阻塞中恢
复,或者被其他线程抢占
运行
(Running)
线程正在运行
阻塞
(Blocked)
线程等待外部事件发生而无法运行,如I/O操作
终止
(Terminated)
线程完成,或退出,或被取消
09-1 | 并发编程基本概念
  • 就绪态:线程有资格被调度,有资格分配cpu时间
  • 运行态:就绪态线程分配cpu时间后的状态,时间片用完后,线程就回到就绪态
    • 阻塞态:线程运行过程中,如果需要等待 运行资源,则进入阻塞态
      • 阻塞态线程如果条件满足,则回到 就绪态
    • 终止态:线程运行中,被取消或者执行完成,进入终止态

线程启动

运行程序会启动一个解释器进程,在这个进程中派生线程,线程共享一个解释器进程。

import threading


def add():
    print('add ~~~~~~~~~~')


t1 = threading.Thread(target=add, name='add')   # 创建一个线程实例

t1.start()   # 启动线程
参数名含义
target线程调用的对象,就是目标函数
name为线程起个名字
args为目标函数传递实参,元组
kwargs为目标函数关键字传参,字典

线程的工作就是执行 target 函数。

线程只能start一次,如果要再次start 需要重新实例化对象。

线程退出

Python没有提供线程退出的方法,线程在下面情况时退出
1、线程函数内语句执行完毕
2、线程函数中抛出未处理的异常

需要注意的是,线程虽然异常退出,但是结束的状态码依然是进程的状态码。

09-1 | 并发编程基本概念

线程传参

因为线程就是执行target函数,所以线程的传参其实就是函数传参,通过args 和 kwargs传参,args传递的是元组,kwargs则是传字典。

import threading


def add(x, y):
    print('add ~~~~~~~~~~{} + {} = {}'.format(x, y, x + y))
    print('in add {}'.format(threading.get_ident()))    # 在线程内,获取线程id


t1 = threading.Thread(target=add, name='add', args=(3,), kwargs={'y': 4})   # 创建一个线程对称

t1.start()
print(t1.ident, t1.name)    # 获取线程的id, 名字
09-1 | 并发编程基本概念

线程属性和方法

名称含义
current_thread()返回当前线程对象
main_thread()返回主线程对象
active_count()当前处于alive状态的线程个数
enumerate()返回所有活着的线程的列表,不包括已经终止的线程和未开始的线程
get_ident()返回当前线程的ID,非0整数

active_count、enumerate方法返回的值还包括主线程。

Thread实例的属性和方法

名称含义
name只是一个名字,只是个标识,名称可以重名。getName()、setName()获取、设置这
个名词
ident线程ID,它是非0整数。线程启动后才会有ID,否则为None。线程退出,此ID依旧可
以访问。此ID可以重复使用
is_alive()返回线程是否活着

start() 和 run() 区别

需要注意的是:在解释器执行任何函数时,如果函数不返回,解释器不会继续向下执行。

主线程中通过start() 调用系统调用,启动一个工作线程后,并执行工作函数。satrt() 函数就返回了(此时start不会关心 工作线程是否执行完成),主线程也就不会阻塞,worker线程和主线程继续执行。

而执行run() 方法,并不会创建新的工作线程,它的所有执行都是在主线程上执行的。此时主线程必须等待 run方法执行完成并return后才能继续执行下去,也就是此时的主线程 阻塞 了。

start() 方法会创建一个新线程,并在这个新线程上,调用run()方法,run方法来执行 函数。

09-1 | 并发编程基本概念

可以看到使用run方法后,没有创建新的线程,run方法依然是在主线程上运行的。所以执行run方法,会阻塞主线程运行。

start,run方法为什么只能执行一次分析

在主线程中,start() 和 run() 方法只能执行一次,如果要重新执行的话,需要在重新实例化线程对象,可以从start和run的源码中,查看怎么限制的只能执行一次?

start() 方法

在python37\Lib\threading.py 第847行,如果设置了self._started.is_set,就抛出异常,这里 的self就是实例化的线程对象。

09-1 | 并发编程基本概念

run方法:

09-1 | 并发编程基本概念

run方法是每次执行完成后就删除 target,args,kwargs属性,下次执行就无法传入target等属性,造成无法执行。




https://www.hugbg.com/archives/2738.html
逸兴
  • 本文由 发表于 2020-06-0118:52:23
  • 除非特殊声明,本站文章均为原创,转载请务必保留本文链接
01-1 数据类型 基础语法

01-1 数据类型

第一章 数据类型 使用type() 函数可以查看数据类型 1.1 字符串 str 字符串是使用单引号或双引号括起来的任意文本。 比如'abc', '123'等 字符串类型 字符串类型用str表示 st...
09-5 | asyncio基本使用 并发编程

09-5 | asyncio基本使用

第一节 关于asyncio asyncio 在3.4 版本中加入到标准库, asyncio基于selector实现, 看似库, 其实是个框架, 包含异步IO, 事件循环, 协程, 任务等内容。 通过a...
09-4 | 全局解释器锁 & 多进程 & 池 并发编程

09-4 | 全局解释器锁 & 多进程 & 池

GIL CPython 在解释器进程级别有一把锁,叫做GIL,即全局解释器锁。 GIL 保证CPython进程中,只有一个线程执行字节码。甚至是在多核CPU的情况下,也只允许同时只能 有一个CPU核心...
匿名

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: