Python学习---线程基础学习


线程基础

什么是线程(thread)

线程是​CPU​调度能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流​[换言之,线程就是一堆指令集合]​,一个进程中可以并发多个线程,每条线程并行执行不同的任务

线程的执行特性


线程只有 3 个基本状态:就绪,执行,阻塞。

线程存在 5 种基本操作来切换线程的状态:派生,阻塞,激活,调度,结束。


什么是进程(Process)
​进程,是并发执行的程序在执行过程中​操作系统​分配和管理资源的基本单位,是一个动态概念,竟争计算机系统资源的基本单位。每一个进程都有一个自己的地址空间,即进程空间或(虚空间)。进程空间的大小 只与处理机的位数有关

进程开辟子进程,子进程完全Copy父进程,比如父进程占用20M,子进程也占用20M,所以开进程比开线程更消耗资源

线程与进程的区别


线程共享创建它的进程的地址空间; 进程有自己的地址空间。

线程可以直接访问其进程的数据段; 进程拥有其父进程的数据段的自己的副本。

线程可以直接与其进程的其他线程通信; 进程必须使用进程间通信与兄弟进程进行通信。

新线程很容易创建; 新线程需要重复父线程。​比如父进程占用20M,子进程也占用20M,所以开进程比开线程更消耗资源

线程之间可以相互操作,进程之间不可以

对主线程的更改(取消,优先级更改等)可能会影响进程的其他子线程线程; 父进程的更改不会影响子进程。


问:线程执行快还是执行进程快?[陷阱题]

答: 一样快,跑的内容是一样的

Python可以创建多进程,不严格来说因为有GIL,Python没有多线程,但是可以利用多进程来解决[多进程下不能实现数据共享,可以通过其他解决,协程,堆等方案]实现CPU多核的利用

如果在py里面,任务是IO密集型[不是一直调用CPU执行任务,会有sleep等IO阻塞],多线程,如果计算密集型,可以考虑C开发

线程创建

线程的创建:


1. 直接调用,threading.Thread(target=sayhi,args=(1,)

2. 继承式调用:


直接调用:

import time
import threading
begin=time.time()
def bar(n):
print('bar%s'%n)
time.sleep(3)
def foo(n):
print('foo%s' %n)
time.sleep(2)

t1 = threading.Thread(target=bar, args=(1,)) # 创建t1线程对象
t2 = threading.Thread(target=foo, args=(2,)) # 创建t2线程对象
t1.start() # 线程启动,开始抢占CPU资源
t2.start() # 线程启动,开始抢占CPU资源
end=time.time()
t1.join() # 线程阻塞,执行完天t1后执行主线程
t2.join() # 线程阻塞,执行完天t2后执行主线程
end2 = time.time()
print('此时有3个线程,主线程,t1线程, t2线程')
print(end-begin)
print(end2-begin)

Python学习---线程基础学习_Python学习

继承式调用:

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()

线程常用方法

Thread.join():​在子线程完成运行之前,这个子线程的父线程将一直被阻塞。

import threading
from time import ctime,sleep
import time

def music(func):
for i in range(2):
print ("Begin listening to %s. %s" %(func,ctime()))
sleep(4)
print("end listening %s"%ctime())

def move(func):
for i in range(2):
print ("Begin watching at the %s! %s" %(func,ctime()))
sleep(5)
print('end watching %s'%ctime())

threads = []
t1 = threading.Thread(target=music,args=('七里香',))
threads.append(t1)
t2 = threading.Thread(target=move,args=('阿甘正传',))
threads.append(t2)

if __name__ == '__main__':

for t in threads:
t.start() # t1.start(). t2.start()
# t.join() # 串行执行,t1.start()后,进入t1.join()等到t1执行完后在执行t2
# t.join() # Python中默认取最后一个for循环的t2,等价于t2.join()
# t1.join() # 主线程的print()会在第8秒出,最后打印end movies
t.join() # t2.join(),t2执行end后程序结束
print ("all over %s" %ctime())

setDemaon(True):

将线程声明为守护线程,必须在start() 方法调用之前设置, 如果不设置为守护线程程序会被无限挂起。这个方法基本和join是相反的。当我们在程序运行中,执行一个主线程,如果主线程又创建一个子线程,主线程和子线程 就分兵两路,分别运行,那么当主线程完成想退出时,会检验子线程是否完成。如果子线程未完成,则主线程会等待子线程完成后再退出。但是有时候我们需要的是 只要主线程完成了,不管子线程是否完成,都要和主线程一起退出,这时就可以 用setDaemon(True)

import threading
from time import ctime,sleep
import time

def music(func):
for i in range(2):
print ("Begin listening to %s. %s" %(func,ctime()))
sleep(4)
print("end listening %s"%ctime())

def move(func):
for i in range(2):
print ("Begin watching at the %s! %s" %(func,ctime()))
sleep(5)
print('end watching %s'%ctime())

threads = []
t1 = threading.Thread(target=music,args=('七里香',))
threads.append(t1)
t2 = threading.Thread(target=move,args=('阿甘正传',))
threads.append(t2)

if __name__ == '__main__':
# t2.setDaemon(True) # 执行t1后就结束程序,也就是说不执行 print('end watching %s'%ctime())
for t in threads:
t.setDaemon(True) # 将线程声明为守护线程,必须在start() 方法调用之前设置
t.start() # t1.start(). t2.start()
print ("all over %s" %ctime())

Python学习---线程基础学习_主线程_02

Thread提供线程的方法

thread 模块提供的其他方法:
threading.currentThread(): 返回当前的线程变量。
threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
除了使用方法外,线程模块同样提供了Thread类来处理线程,Thread类提供了以下方法:
run(): 用以表示线程活动的方法。
start():启动线程活动。
join([time]): 等待至线程中止。这阻塞调用线程直至线程的join() 方法被调用中止-正常退出或者抛出未处理的异常-或者是可选的超时发生。
isAlive(): 返回线程是否活动的。
getName(): 返回线程名。
setName(): 设置线程名。

python 的GIL(Global Interpreter Lock)

解释器原因: 由于Cpython解释的原因,同一时刻只能解释器只能调用一个线程,可以理解Python没有多线程

CPython实现细节:在CPython中,由于全局解释器锁定,只有一个线程可以一次执行Python代码(即使某些面向性能的库可能会克服此限制)。 如果您希望您的应用程序更好地利用多核计算机的计算资源,建议您使用多处理。 但是,如果要同时运行多个I / O密集型任务的话,线程仍然是一个合适的模型。



作者:​​小a玖拾柒​​​ ​

-------------------------------------------

个性签名: 所有的事情到最後都是好的,如果不好,那說明事情還沒有到最後~

本文版权归作者【​​小a玖拾柒​​​】,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利!