1 线程
线程是进程中执行运算的最小单位,亦即执行处理机调度的基本单位。如果把进程理解为在逻辑上操作系统所完成的任务,那么线程表示完成该任务的许多可能的子任务之一。
基本概念:
线程:进程中的每个子任务,不能独立存在
进程:独立的所有子任务的集合
线程,进程:目的都是想同时完成任务
特点:
进程的特点:独立(内存独立,cpu使用独立)启动进程开销大(速率低),进程之间很难共享数据,和数据通信,数据安全高。
线程的特点:依赖进程(内存共享,CPU使用独立)启动开销小,线程之间共享数据容易,方便通信,线程不安全。
单线程
使用python编写单线程的程序,实际上就是按顺序执行程序的方式,即只有一个子任务完成后,才能执行后面的任务。
多线程
python支持使用多线程,程序代码可以在一个进程空间操作管理多个执行的线程。
线程生命周期
在线程的生命周期中,它要经过新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)五种状态。尤其是当线程启动以后,它不能一直“霸占”着CPU独自运行,所以CPU需要在多条线程之间切换,于是线程状态也会多次在运行、阻塞之间切换。
如果处于就绪状态的线程获得了CPU,开始执行run方法的线程执行体,则该线程处于运行状态。
当发生如下情况下,线程将会进入阻塞状态:
1 线程调用sleep方法主动放弃所占用的处理器资源。
2 线程调用了一个阻塞式IO方法,在该方法返回之前,该线程被阻塞。
3 线程试图获得一个同步监视器,但该同步监视器正被其他线程锁持有。
4线程在等待某个通知(notify)。
5程序调用了线程的suspend方法将该线程挂起。不过这个方法容易导致死锁,所以程序应该尽量避免使用该方法。
当前正在执行的线程被阻塞之后,其他线程就可以获得执行的机会了。被阻塞的线程会在合适时候重新进入就绪状态,注意是就绪状态而不是运行状态。也就是说被阻塞线程的阻塞解除后,必须重新等待线程调度器再次调度它。
针对上面的几种情况,当发生如下特定的情况将可以解除上面的阻塞,让该线程重新进入就绪状态:
1 调用sleep方法的线程经过了指定时间。
2 线程调用的阻塞式IO方法已经返回。
3 线程成功地获得了试图取得同步监视器。
4 线程正在等待某个通知时,其他线程发出了一个通知。
5 处于挂起状态的线程被调用了resume恢复方法。
线程状态转换图:
2 python实现线程有两种方式:函数和类
Python2:thread
Python3:_thread
threading(功能相比_thread更强大,推荐使用)
函数方法
线程必须依赖函数实现,不能单独运行。当函数结束,线程结束。所以如果想让线程一直运行,就要想办法让程序不结束,比如在程序最后加一个input(),或死循环。
构建一个新的进程
threading._start_new_thread ( function, args[, kwargs] )
参数说明:
function - 线程函数。
args - 传递给线程函数的参数,他必须是个tuple类型。
kwargs - 可选参数。
import threading
import time
def someting():
for i in range(1,11):
print(i)
time.sleep(1)
threading._start_new_thread(someting,())
print("main")
input()
执行结果:
main
1
2
3
4
5
threading 模块除了包含 _thread 模块中的所有方法外,还提供的其他方法:
threading.currentThread(): 返回当前的线程变量。
threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
类方法
线程类:1.继承threading.Thread
2.写构造方法,且必须调用父类的构造方法
3.重写父类的run方法,会在start之后自动调用
4.实现开始方法,如果重写了start()方法,一定要调用父类的start()
需要调用start()方法才可以执行
Thread类提供了以下方法:
run(): 用以表示线程活动的方法。
start():启动线程活动。
join([time]): 等待至线程中止。这阻塞调用线程直至线程的join() 方法被调用中止-正常退出或者抛出未处理的异常-或者是可选的超时发生。
isAlive(): 返回线程是否活动的。
getName(): 返回线程名。
setName(): 设置线程名。
import threading
import time
class Mythread(threading.Thread):
def __init__(self,name):
threading.Thread.__init__(self)
self.name=name
print("Mythread")
def run(self):# 把要执行的代码写到run函数里面 线程在创建后会直接运行run函数
for i in range(1,6):
print(self.name,i)
time.sleep(1)#单位为秒
def start(self):
print("开始Mythread")
threading.Thread.start(self)
t1=Mythread('t1')
t2 = Mythread('t2')
t1.start()
t1.join()#加入并阻断主线程的执行,即执行完才执行主线程,可以加时间,join(3)阻断3秒结束阻断
t2.start()
结果:
Mythread
Mythread
开始Mythread
t1 1
t1 2
t1 3
t1 4
t1 5
开始Mythread
t2 1
t2 2
t2 3
t2 4
t2 5
线程同步
当多个线程同时进行任务时,为了保证不会有多个线程同时对同一个数据进行操作造成不可预料的后果,所以有了锁的概念,我们通过锁来使多线程任务更加安全。
由于并发的问题,需要加锁:
锁当然有锁定和未锁定两种状态,当一个线程要访问共享数据时,必须要先获得锁定,如果已经有别的线程获得锁定,那么就进入暂停状态,等别的线程把锁释放后,再进行操作。
Condition:更精确的控制锁,提供了四个方法,上锁(acquire()),等待(wait()),解锁(release()),唤醒(notify(),notify_all())
lock = threading.Lock()
cond = threading.Condition(lock=lock)
使用Thread对象的Lock和Rlock可以实现简单的线程同步,这两个对象都有acquire方法和release方法,对于那些需要每次只允许一个线程操作的数据,可以将其操作放到acquire和release方法之间。
import threading
import time
class myThread(threading.Thread):
def __init__(self, threadID, name, counter):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.counter = counter
def run(self):
print("Starting " + self.name)
# 获得锁,成功获得锁定后返回True
# 可选的timeout参数不填时将一直阻塞直到获得锁定
# 否则超时后将返回False
threadLock.acquire()
print_time(self.name, self.counter, 3)
# 释放锁
threadLock.release()
def print_time(threadName, delay, counter):
while counter:
time.sleep(delay)
print("%s: %s" % (threadName, time.ctime(time.time())))
counter -= 1
threadLock = threading.Lock()
threads = []
# 创建新线程
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)
结果:
Starting Thread-1
Starting Thread-2
Thread-1: Sun Jul 15 19:32:54 2018
Thread-1: Sun Jul 15 19:32:55 2018
Thread-1: Sun Jul 15 19:32:56 2018
Thread-2: Sun Jul 15 19:32:58 2018
Thread-2: Sun Jul 15 19:33:00 2018
Thread-2: Sun Jul 15 19:33:02 2018
更精确的控制锁
import threading
import time
class Thread1(threading.Thread):
def run(self):
for i in range(1,6):
if i==3: #若是3 则上锁让其等待直到被唤醒
cond.acquire() #上锁
cond.wait() #等待
cond.release() #解锁
print(i)
time.sleep(1) #休眠一秒
class Thread2(threading.Thread):
def run(self):
for i in range(10,5,-1):
print(i)
time.sleep(1)
cond.acquire()
cond.notify() #唤醒
cond.release()
lock = threading.Lock()
cond = threading.Condition(lock=lock)
t1 = Thread1()
t2 = Thread2()
t1.start()
t2.start()
结果:
1
10
2
9
8
7
6
3
4
5