目录
线程与进程概念
进程
进程(Process)是计算机中的程序关于某数据集合上的一次运行活动。是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中。
线程
线程(Thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流。
线程与进程状态
当进程退出时该进程所产生的线程都会被强制退出并清除。线程可与属于同一进程的其它线程共享进程所拥有的全部资源,
但是其本身基本上不拥有系统资源,只拥有一点在运行中必不可少的信息(如程序计数器、一组寄存器和栈)。
多进程适合在CPU 密集型操作(cpu 操作指令比较多,如科学计算,位数多的浮点运算)
多线程适合在IO 密集型操作(读写数据操作较多的,比如爬虫)
线程是并发,进程是并行;进程之间相互独立,是系统分配资源的最小单位,同一个进程中的所有线程共享资源。
协程
协程是用户态的轻量级线程,调度有用户控制,拥有自己的寄存器上下文和栈,切换基本没有内核切换的开销,切换灵活。
总体
- 一个进程中可以并发多个线程,每条线程并行执行不同的任务。
- 进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。
- 线程是属于进程的,线程运行在进程空间内,同一进程所产生的线程共享同一内存空间。
- 线程是并发,进程是并行。
线程例
基本操作
#线程基本操作
import threading
import time
def cxk(n):
print('运行线程:',n)
for i in range(10):# 创建10个线程
t = threading.Thread(target=cxk, args=(i,)) # 线程运行的函数和参数
t.setDaemon(True) # 设置为守护线程(在主线程线程结束后自动退出,默认为False即主线程线程结束后子线程仍在执行)
t.start() # 启动线程
输出:
运行线程: 0
运行线程: 1
运行线程: 2
运行线程: 3
运行线程: 4
运行线程: 5
运行线程: 6
运行线程: 7
运行线程: 8
运行线程: 9
全局变量操作
import threading,time
def cxk(n):
global j
j+=1
time.sleep(1)
print('运行线程:%s,变量j的情况:%s'%(n,j))
j=-1
for i in range(10):# 创建10个线程
t = threading.Thread(target=cxk, args=(i,)) # 线程运行的函数和参数
t.setDaemon(True) # 设置为守护线程(在主线程线程结束后自动退出,默认为False即主线程线程结束后子线程仍在执行)
t.start() # 启动线程
如果处理速度比较快并不会出现脏数据的情况
>>>
运行线程:0,变量j的情况:0
运行线程:1,变量j的情况:1
运行线程:2,变量j的情况:2
运行线程:3,变量j的情况:3
运行线程:4,变量j的情况:4
运行线程:5,变量j的情况:5
运行线程:6,变量j的情况:6
运行线程:7,变量j的情况:7
运行线程:8,变量j的情况:8
运行线程:9,变量j的情况:9
但是当我们每个进程睡眠加个进去,time.sleep(1),相当于处
理速度比较慢的时候,就会变成下面这样,因为操作的全部线程执行完后第一个线程打印部分还在睡眠,当他打印j变量的时候全部线程已经执行完毕了
>>>
运行线程:0,变量j的情况:9
运行线程:1,变量j的情况:9
运行线程:3,变量j的情况:9
运行线程:5,变量j的情况:9
运行线程:4,变量j的情况:9
运行线程:8,变量j的情况:9
运行线程:7,变量j的情况:9
运行线程:2,变量j的情况:9
运行线程:9,变量j的情况:9
运行线程:6,变量j的情况:9
解决办法就是加锁
# 线程锁
import threading,time
def cxk(n):
lock.acquire() # 开始锁
global j
j+=1
time.sleep(1)
print('运行线程:%s,变量j的情况:%s'%(n,j))
lock.release() # 结束锁
j=-1
lock = threading.RLock() # 定义线程锁
for i in range(10):# 创建10个线程
t = threading.Thread(target=cxk, args=(i,)) # 线程运行的函数和参数
t.setDaemon(True) # 设置为守护线程(在主线程线程结束后自动退出,默认为False即主线程线程结束后子线程仍在执行)
t.start() # 启动线程
#输出
>>>
运行线程:0,变量j的情况:0
运行线程:1,变量j的情况:1
运行线程:2,变量j的情况:2
运行线程:3,变量j的情况:3
运行线程:4,变量j的情况:4
运行线程:5,变量j的情况:5
运行线程:6,变量j的情况:6
运行线程:7,变量j的情况:7
运行线程:8,变量j的情况:8
运行线程:9,变量j的情况:9
那么多线程处理文件的时候会出现文件资源出现问题的情况吗?我们来为每个线程同时向同一个文件写十句话。
import threading,time
def cxk(n):
with open('cxk.txt','a+') as f:
for i in range(0,10):
time.sleep(1)
f.write('我是线程:%s \n'%str(n))
print('线程:%s 写入成功'%str(n))
for i in range(10):# 创建10个线程
t = threading.Thread(target=cxk, args=(i,)) # 线程运行的函数和参数
t.setDaemon(True) # 设置为守护线程(在主线程线程结束后自动退出,默认为False即主线程线程结束后子线程仍在执行)
t.start() # 启动线程
由图可以看出没什么问题,并没出现同一文件不能处理,我们加大处理速度试试,睡眠 。
也没什么问题,就是线程完成速度不一样,并没出现第一个线程写完第一句第二个线程接着写第二句,但是这样看起来不像是并行,倒像是串行的。。。我就在想会不会是在并行的情况下,第一个线程睡眠时第二个线程也是同时处于睡眠状态,所以写入也是按顺序的。。。不过我在网上得到下面这一段话:
"""
在Python的原始解释器CPython中存在着GIL,因此在解释执行Python代码时,会产生互斥锁来限制线程对共享资源的访问,直到解释器遇到I/O操作或者操作次数达到一定数目时才会释放GIL,所以有GIL效果就是:** 一个进程内同一时间只能允许一个线程进行运算 ** (这尼玛不就是单线程吗?)
python的多线程只能在一个核心上跑(创始人没想到会有多核出现),就是单核的上下文切换,所以很鸡肋。
于是协程在python大展拳脚,好多框架都是使用协程来解决多任务的,而不是线程(scrapy,tornado)。
"""
最后似懂非懂,也就是说到底python的多线程就不是真正的并行。。。
不过python的多线程确实能提升效率,并且使用tkinter时打开不同窗口使用多线程不会产生卡住状态,这篇博客也采用多线程处理,效率大大提升,还有以前的tkinter练习也基本用到了,有兴趣可以去看看。
进程例
进程我用的不多,因为我用notebook运行多进程时只有主进程会显示,子进程调用的函数没反应,只能打包成py文件去cmd下运行。
#写入该句话,运行时直接生成py文件
%%writefile cxkkxc.py
import multiprocessing,time
"""
进程各自持有一份数据,默认无法共享数据
当创建进程时(非使用时),共享数据会被拿到子进程中,当进程中执行完毕后,再赋值给原值。
"""
def cxk(n):
with open('cxk2.txt','a+') as f:
for i in range(0,10):
f.write('我是进程:%s \n'%str(n))
time.sleep(1)
print('进程:%s 写入成功'%str(n))
if __name__ == "__main__":
for i in range(1,3):
p = multiprocessing.Process(target = cxk, args = (i,))
p.start()
print("p.pid:", p.pid)
print ("p.name:", p.name)
print ("p.is_alive:", p.is_alive())
这里我试用的也是打开文件的形式,只不过是以with open打开的,加了睡眠没有加锁也没出现资源共享问题,搞不懂搞不懂,可能with open 会自动关闭文件吧,每写完一句关闭第二个进程再打开写入。以后再探究探究。最近可能没空咯,参加了一个比赛,等忙完这阵再继续更。