1、线程理论知识

  概念:指的是一条流水线的工作过程的总称,是一个抽象的概念,是CPU基本执行单位。

  进程和线程之间的区别:

    1. 进程仅仅是一个资源单位,其中包含程序运行所需的资源,而线程就相当于车间的流水线,负责执行具代码。

    2. 每个进程至少包含一个线程,由操作系统自动创建,称之为主线程

    3. 每个进程可以有任意数量的线程

    4.创建进程的开销要比创建进程小得多

    5. 同一进程的线程间数据是共享的

    6.线程之间是平等的,没有子父级关系,同一进程下的各线程的PID相同

    7. 创建线程的代码可以写在任意位置,不一定非要在main函数下。

  为什么使用线程:

    提高程序执行效率

2、开启线程的两种方式

  和进程类似,但是开启方式不一定非要建在main函数下。

# 第一种方式,实例化 Thread
# from threading import Thread
#
# def task():
# print("subthread is running....")
#
# t = Thread(target=task)
# t.start()
# print('main is over....') # 第二种方式,继承Thread类 from threading import Thread class MyThread(Thread):
def run(self):
print("subthread is running....")

两种方式

3、主线程和子线程之间的关系

  1. 主线程任务执行完毕后,主线程会等待所有子线程全部执行完毕后结束

  2. 在同一进程中,所有线程都是平等的,没有子父级关系

# 验证主线程代码执行完后会不会立即结束,
import random
import time
import threading
from threading import Thread
def task(name):
print("%s is running..." % name)
time.sleep(random.randint(1, 3))
print(threading.enumerate())
print("%s is over....." % name) t = Thread(target=task, args=('aaa',))
t.start() print('main over....')

# 验证主线程代码执行完后会不会立即结束,

4、验证线程和进程之间的区别

from threading import Thread
import time def task():
global num
time.sleep(1)
num -= 1
num = 10
t = Thread(target=task,)
t.start()
t.join()
print(num)

同一进程中线程的数据是可以共享的

from multiprocessing import Process
from threading import Thread
import time def task():
pass def expense(cls):
"""用来测试线程或进程创建开销"""
lis = []
start = time.time()
for i in range(50):
p = cls(target=task,)
p.start()
lis.append(p)
for p in lis:
p.join()
return time.time()-start

创建线程的开销要比创建进程小的多

5、线程的安全问题

 1.互斥锁

  数据共享必然会造成竞争,竞争就会造成数据错乱问题。

  解决办法:和进程一样,加互斥锁。

from threading import Thread, Lock
import time num = 10 def task(lock):
global num
lock.acquire()
a = num
time.sleep(0.5)
num = a-1
lock.release() ts = []
lock = Lock()
for i in range(10):
t = Thread(target=task,args=(lock,))
t.start()
ts.append(t)
for t in ts:
t.join()
print(num)

加互斥锁,保证数据安全

  2.死锁

  死锁不是一种锁,而是一种锁的状态,

  一般出现死锁的情况有两种:

    1. 对同一把锁多次acquire.(使用RLOCK锁,代替LOCK)

    2. 两个或两个以上的进程或线程在执行过程中,因争夺资源造成的相互等待现象。(解决办法:能不加最好不加,要加就只加一把)

from threading import Thread, Lock
import time def task1(name, locka, lockb):
locka.acquire()
print("%s拿到a锁"%name)
time.sleep(0.3)
lockb.acquire()
print('%s拿到b锁'%name)
lockb.release()
locka.release()
def task2(name, locka, lockb):
lockb.acquire()
print("%s拿到b锁"%name)
time.sleep(0.3)
locka.acquire()
print('%s拿到a锁'%name)
locka.release()
lockb.release() locka = Lock()
lockb = Lock()
t1 = Thread(target=task1, args=('t1', locka, lockb))
t2 = Thread(target=task2, args=('t2', locka, lockb))
t1.start()
t2.start()

死锁的第二种情况的示例

  3.可重入锁

  只能解决同一线程多次执行acquire情况。

  只有一个线程所有的acquire都被释放,其他线程才能拿到这个锁。

  也会发生死锁现象。

from threading import Thread, RLock

lock = RLock()
lock.acquire()
lock.acquire()
lock.acquire()
lock.acquire() print("over")
lock = RLock() def task1():
lock.acquire()
print('task1')
def task2():
lock.acquire()
print('task2') Thread(target=task1).start()
Thread(target=task2).start()

示例

  4. 信号量

  也是一种锁,用来控制同一时间,有多少线程可以提供并发访问,不是用来处理线程安全问题

from threading import Semaphore, Thread
import time
s_lock = Semaphore(3) def task():
s_lock.acquire()
time.sleep(1)
print("run.....")
s_lock.release() for i in range(20):
t = Thread(target=task)
t.start()

示例

6、守护线程

  守护线程在所有非守护线程结束后结束。

import threading
from threading import Thread
import time def task1():
print('thread-1 is running...')
time.sleep(3)
print('thread-1 over....') def task2():
print('thread-2 is running...')
time.sleep(1)
print('thread-2 over....') if __name__ == '__main__':
t1 = Thread(target=task1,)
t2 = Thread(target=task2,)
t1.setDaemon(True)
t1.start()
t2.start()
print(t1.ident)
print(threading.enumerate())
print("main over...")

示例

7、GIL

  全局解释器锁,是一互斥锁,只有在Cpython解释器存在。

  为什么需要:因为一个python.exe进行运行只有一份解释器,如果这个进程开启的多个线程都要执行代码,多线程之间就要竞争解释器,一旦竞争就有可能出现问题。

  带来的好处:保证了多线程同时访问解释器时的数据安全问题。

  带来的问题:同一时间只有一个线程访问解释器,使得多线程无法真正的并发

  出现的原因:默认情况下,一个进程只有一个线程不会是不会出问题,但不要忘了还有GC线程,一旦出现多个线程就可能出现问题,所以当初就简单粗暴的加上了GIL锁

  GIL加锁和解锁时机:

    加锁:在调用解释器时立即加锁

    解锁:当前线程遇到IO时释放,或者当前线程执行超过设定值释放(py2计算的是执行代码的行数,py3中计算的是时间)

  解决办法:使用多进程或使用其他的python解释器

8、线程池和进程池

  一种容器,本质十一存储线程或进程的列表

  为什么使用? 因为服务器不能无限开启线程或进程,所以需要对线程数量加以控制,线程池就是帮我们完成线程/进程的创建、销毁以及任务分配

  特点:

    线程池在创建时不会开启线程,

    等到任务提交时,如果没有空闲线程,并且已存在的线程数量小于最大值,开启新线程,

    线程开启后不会关闭,直到进程全部结束为止

  (线程池的建立也要建在main函数下

from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
pool= ProcessPoolExecutor(maxsize),创建进程池,maxsize为最大进程个数 res = pool.submit(task, 'a'), 提交任务 res.result(timeout),接收调用的返回值,timeout为超时时间,超时报错
该函数是阻塞函数,会一直等待任务执行完毕
pool.shutdown(wait),所有任务执行完毕,阻塞函数
wait=True, 等待池内所有任务执行完毕后回收资源才继续
wait=False,立即返回,并不会等待池内的任务执行完毕

方法和属性

from concurrent.futures import ThreadPoolExecutor
import time def task(num):
time.sleep(0.5)
print("%s is running....."%num)
return num**2 pool = ThreadPoolExecutor()
ress = []
for i in range(10):
res = pool.submit(task, i)
ress.append(res) pool.shutdown(wait=False) for i in ress:
print(i.result()) print('over')

示例

9、同步异步阻塞非阻塞

  阻塞和非阻塞都是指程序的运行状态

    阻塞:当程序执行遇到IO操作,无法继续执行代码

    非阻塞:程序执行没有遇到IO操作,或通过某种方式,使程序遇到了也不会停在原地,还可以继续执行

  同步异步指的是提交任务的方式

    同步:发起任务后必须原地等待任务执行完成,才可以继续执行

    异步:发起任务后不用等待任务执行,可以立即执行其他操作

    异步效率高于同步,发起异步任务方式:就是多线程和多进程

  同步和阻塞的不同:阻塞一定使CPU已经切换,同步虽然在等待,但CPU没有切走,还在当前进程中执行其他任务

10、异步回调

  其实说的是回调函数,给异步任务绑定一个函数,当任务完成时会自动调用该函数。

  优点:不用原地等待,任务结果立即获取

  线程池或进程池内内的调用回调函数方法add_done_back(), 且回调函数必须有且只有一个参数,就是调用对象本身。

  线程池的回调函数是在子线程内执行,

  进程池的回调函数是在主进程下执行

import requests
from concurrent.futures import ThreadPoolExecutor
import threading def product_data(url):
data = requests.get(url)
return data.text,url def parser_data(f):
res = f.result()
print(len(res[]), res[], "当前线程", threading.current_thread()) if __name__ == '__main__':
urls = ['http://www.baidu.com','https://www.cnblogs.com/ywsun/', 'https://www.processon.com/']
pool = ThreadPoolExecutor()
for url in urls:
f = pool.submit(product_data, url)
f.add_done_callback(parser_data)

线程池异步调用

import requests
from concurrent.futures import ProcessPoolExecutor
import os
def product_data(url):
data = requests.get(url)
return data.text,url def parser_data(f):
res = f.result()
print(len(res[]), res[], ", callback pid", os.getpid() ) if __name__ == '__main__':
urls = ['http://www.baidu.com','https://www.cnblogs.com/ywsun/', 'https://www.processon.com/']
pool = ProcessPoolExecutor()
print('main process', os.getpid())
for url in urls:
f = pool.submit(product_data, url)
f.add_done_callback(parser_data)

进程池异步调用

11、线程队列

  queue 该模块下提供了一些常见的数据容器,仅仅是容器,没有数据共享特点

  Queue,先进先出

  LifoQueue,后进先出

  PriorityQueue,可设置优先级的队列。插入元组,第一个元素是优先级,可是数字、字母,对应的数值越小优先级越高

import queue

q=queue.PriorityQueue()
#put进入一个元组,元组的第一个元素是优先级(通常是数字,也可以是非数字之间的比较),数字越小优先级越高
q.put((,'a'))
q.put((,'b'))
q.put((,'c')) print(q.get())
print(q.get())
print(q.get()) # 如果是字符,按照ASCII表来排序
q.put(('a', "sfsja"))
q.put(('b', "sdfsdf"))
q.put(('A', "sdfsdf"))
q.put(('ae', "sdfsdf"))
q.put(('ab', "sdfsdf"))
print(q.get())
print(q.get())
print(q.get())
print(q.get())
print(q.get())

PriorityQueue示例

12、事件

  用于线程间通讯,线程间本就是数据共享,也就是即使没有事件,也没有问题

  线程之间,执行流程是完全独立的,一些时候可能需要知道另一个进程发生了什么,然后采取一些行动。
  方法:
    event.isSet(),返回event的状态值;
    event.wait(), 如果event.isSet()==False将阻塞进程。
    event.set(), 设置event的状态Ture,将所有阻塞池的线程激活,进入就绪状态。等待操作系统调度。
    even.clear(), 恢复event的状态位False。
from threading import Thread, Event
import time
import random
boot= Event() def server():
print('启动服务器。。。。')
time.sleep(random.randint(,))
print('服务器运行。。。。。')
boot.set() def connect():
print('开始尝试连接')
boot.wait()
print('连接成功') t1 = Thread(target=server)
t1.start() t2 = Thread(target=connect)
t2.start()

 

    

python并发编程之多线程基础知识点的更多相关文章

  1. python并发编程之多进程基础知识点

    1.操作系统 位于硬件与应用软件之间,本质也是一种软件,由系统内核和系统接口组成 和进程之间的关系是: 进程只能由操作系统创建 和普通软件区别: 操作系统是真正的控制硬件 应用程序实际在调用操作系统提 ...

  2. Python并发编程04 /多线程、生产消费者模型、线程进程对比、线程的方法、线程join、守护线程、线程互斥锁

    Python并发编程04 /多线程.生产消费者模型.线程进程对比.线程的方法.线程join.守护线程.线程互斥锁 目录 Python并发编程04 /多线程.生产消费者模型.线程进程对比.线程的方法.线 ...

  3. 29 python 并发编程之多线程理论

    一 什么是线程 在传统操作系统中,每个进程有一个地址空间,而且默认就有一个控制线程 线程顾名思义,就是一条流水线工作的过程,一条流水线必须属于一个车间,一个车间的工作过程是一个进程 车间负责把资源整合 ...

  4. 三 python并发编程之多线程-理论

    一 什么是线程 在传统操作系统中,每个进程有一个地址空间,而且默认就有一个控制线程 线程顾名思义,就是一条流水线工作的过程,一条流水线必须属于一个车间,一个车间的工作过程是一个进程 车间负责把资源整合 ...

  5. python并发编程之多线程理论部分

    阅读目录 一 什么是线程 二 线程的创建开销小 三 线程与进程的区别 四 为何要用多线程 五 多线程的应用举例 六 经典的线程模型(了解) 七 POSIX线程(了解) 八 在用户空间实现的线程(了解) ...

  6. 35、python并发编程之多线程(理论篇)

    一 什么是线程 二 线程的创建开销小 三 线程与进程的区别 四 为何要用多线程 五 多线程的应用举例 六 经典的线程模型(了解) 七 POSIX线程(了解) 八 在用户空间实现的线程(了解) 九 在内 ...

  7. python并发编程之多线程

    一  同步锁 注意: 1线程抢的是GIL锁,GIL锁就是执行权限,拿到权限后才能拿到互斥锁Lock,但是如果发现Lock没有被释放而阻塞,则立即交出拿到的执行权. 2join是等待所有,即整体串行,而 ...

  8. python并发编程之协程知识点

    由线程遗留下的问题:GIL导致多个线程不能真正的并行,CPython中多个线程不能并行 单线程实现并发:切换+保存状态 第一种方法:使用yield,yield可以保存状态.yield的状态保存与操作系 ...

  9. Python并发编程之多线程使用

    目录 一 开启线程的两种方式 二 在一个进程下开启多个线程与在一个进程下开启多个子进程的区别 三 练习 四 线程相关的其他方法 五 守护线程 六 Python GIL(Global Interpret ...

随机推荐

  1. 百度官方CDN公共库(jquery、dojo、Bootstrap)

    CDN公共库是指将常用的JS库存放在CDN节点,以方便广大开发者直接调用.与将JS库存放在服务器单机上相比,CDN公共库更加稳定.高速. 百度公共CDN为您的应用程序提供稳定.可靠.高速的服务,包含全 ...

  2. oracle学习笔记(七) 预编译Statement介绍与使用

    预编译Statement优点 执行效率高 由于预编译语句使用占位符 "?",在执行SQL之前语句会被先发送到Oracle服务器进行语法检查和编译等工作,并将SQL语句加入到Orac ...

  3. log4j日志框架的使用

    java.util.logging.Logger——java 中提供的日志类 实际开发 90% 都是使用 log4j 记录日志,而 Log4j 底层就是 java.util.logging.Logge ...

  4. 面试常问的几个排序和查找算法,PHP实现

    冒泡,快排,二分查找,都是面试常问的几个算法题目,虽然简单,但是一段时间不用的话就很容易忘记,这里我用PHP实现了一下,温故而知新. 排序 冒泡排序 每一次冒出一个最大的值 function bubb ...

  5. openlayers 3方法继承

    之前Web GIS开发使用的ArcGIS API用起来很系统,但是使用开源Web GIS API已经成主流趋势(你懂的~),最近项目想要从ArcGIS API 转到openlayers API,用起来 ...

  6. cesium 之三维漫游飞行效果实现篇(附源码下载)

    前言 cesium 官网的api文档介绍地址cesium官网api,里面详细的介绍 cesium 各个类的介绍,还有就是在线例子:cesium 官网在线例子,这个也是学习 cesium 的好素材. 内 ...

  7. Python数据描述与分析

    在进行数据分析之前,我们需要做的事情是对数据有初步的了解,比如对数据本身的敏感程度,通俗来说就是对数据的分布有大概的理解,此时我们需要工具进行数据的描述,观测数据的形状等:而后才是对数据进行建模分析, ...

  8. 商米D1S一体机设置搜狗手写输入法图解

    按照下图步骤,一步步设置即可,询问全新的时候需要点击允许. 商米应用市场搜索下载搜狗输入法,并安装 安装完成后,点击桌面搜狗输入法,选择启用搜狗输入法,如图 点击启用后,在虚拟键盘中选择搜狗输入法,并 ...

  9. 从0开始的Python学习017Python标准库

    简介 Python标准库使随着Python附带安装的,它包含很多有用的模块.所以对一个Python开发者来说,熟悉Python标准库是十分重要的.通过这些库中的模块,可以解决你的大部分问题. sys模 ...

  10. SQLServer之创建提交读

    事务提交读注意事项 语法:set transaction isolation level read committed. 数据库默认的是两个会话事务之间是提交读. READ COMMITTED指定语句 ...