前言:
python也有线程的概念,原理和操作系统的线程一样,只是语法什么的不一样。
1- 线程相关接口
python线程编程需要导入模块threading。先了解下如何获取线程名字、活动线程个数、主线程。
#!/usr/bin/python
import threading
#获取当前线程对象
cur_thread = threading.current_thread()
print("cur_thread name:{0}".format(cur_thread.name))
#获取活动线程个数
print("active thread cnt:{0}".format(threading.active_count()))
#获取主线程名字
main_thead = threading.main_thread()
print("main_thread name:{0}".format(main_thead.name))
2- 创建线程(threading类)
使用threading.Thread方法创建线程,线程创建后执行start,线程才会运行。threading.Thread参数如下:
threading.Thread(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)
其中常用的有target,用于指定线程入口;name用于指定线程名字;args和kwargs用于给线程传递参数,暂不关心
注意:我们创建线程入口里面一般是个死循环,永远不返回。死循环里做监控、定期执行、条件执行等的任务
示例:
#!/usr/bin/python
import threading
import time
def thread_entry():
while True:
cur_thread = threading.current_thread()
print("cur_thread name:{0}".format(cur_thread.name))
time.sleep(1)
def main():
thread1 = threading.Thread(target=thread_entry, name="thread1")
thread1.start()
if __name__ == "__main__":
main()
3- 创建线程(使用类)
3.1- 使用类创建线程
除了借助thread.Thread创建对象实体,我们还可以自己写个类继承threading.Thread。
示例:
示例中执行thread1.start()会跳转到Mythread.run接口,即run接口重写了start()。为何会这样呢?
#!/usr/bin/python
import threading
import time
class MyThread(threading.Thread):
def __init__(self, name):
super(MyThread, self).__init__(name=name)
#重写threading.Thread.start()接口
def run(self):
cur_thread = threading.current_thread()
while True:
print("cur_thread name:{0}".format(cur_thread.name))
time.sleep(1)
def main():
thread1 = MyThread("thread-1")
thread1.start()
if __name__ == "__main__":
main()
3.2- 创建多个线程如何指定入口?
另外如果我们创建多个线程,如何为每个线程单独指定入口呢?
可以使用下面两种方法,当然应该还有别的方式,欢迎跟帖留言
一种是我们在创建线程对象时,传入线程入口thread_entry(),然后在run接口中self.thread_entry()
另一种时在run中判定线程名字,根据名字执行不同入口。
这里我们偷下懒,让线程1、2入口名字一样。。。。
F1:
#!/usr/bin/python
import threading
import time
class MyThread(threading.Thread):
def __init__(self, target, name):
super(MyThread, self).__init__(name=name)
self.target = target
# 重写threading.Thread.start()接口
def run(self):
self.target()
def thread_entry():
while True:
cur_thread = threading.current_thread()
print("cur_thread name:{0}".format(cur_thread.name))
time.sleep(1)
def main():
thread1 = MyThread(thread_entry, "thread-1")
thread1.start()
thread2 = MyThread(thread_entry, "thread-2")
thread2.start()
if __name__ == "__main__":
main()
F2:
#!/usr/bin/python
import threading
import time
class MyThread(threading.Thread):
def __init__(self, target, name):
super(MyThread, self).__init__(name=name)
#重写threading.Thread.start()接口
def run(self):
cur_thread = threading.current_thread()
if cur_thread.name == "thread-1":
thread_entry()
else:
thread_entry()
def thread_entry():
while True:
cur_thread = threading.current_thread()
print("cur_thread name:{0}".format(cur_thread.name))
time.sleep(1)
def main():
thread1 = MyThread(thread_entry, "thread-1")
thread1.start()
thread2 = MyThread(thread_entry, "thread-2")
thread2.start()
if __name__ == "__main__":
main()
4- join() 等待线程结束
和linux API接口一样,等待线程结束的接口也是join()。当线程1等待线程2结束,线程2返回前,线程1一直是阻塞状态。只有线程2返回,线程1才能继续执行。
应用场景:某个线程需要等待某些事情结束才能继续执行。
举个实际生活中的例子:实际开发过程中,我们要做一个需求:让手机拍照美颜更好看。首先需要市场调研清楚用户需求,调研好后传达给项目。项目才能根据用户反馈做下需求设计。这样需求设计就在市场调研完成后才能开展。
示例:
#!/usr/bin/python
import threading
import time
class MyThread(threading.Thread):
def __init__(self, target, name):
super(MyThread, self).__init__(name=name)
self.target = target
# 重写threading.Thread.start()接口
def run(self):
self.target()
def thread_entry():
cur_thread = threading.current_thread()
for arg in range(5):
print("cur_thread name:{0}, run_cnt:{1}".format(cur_thread.name,arg))
arg += 1
time.sleep(1)
print("cur_thread name:{0} is exit".format(cur_thread.name))
def main():
cur_thread = threading.current_thread()
print("cur_thread name:{0} is run".format(cur_thread.name))
thread1 = MyThread(thread_entry, "thread-1")
thread1.start()
thread1.join()
print("cur_thread name:{0} is exit".format(cur_thread.name))
if __name__ == "__main__":
main()
打印为:
cur_thread name:MainThread is run
cur_thread name:thread-1, run_cnt:0
cur_thread name:thread-1, run_cnt:1
cur_thread name:thread-1, run_cnt:2
cur_thread name:thread-1, run_cnt:3
cur_thread name:thread-1, run_cnt:4
cur_thread name:thread-1 is exit
cur_thread name:MainThread is exit
可以去掉thread1.join对比下不同
5- 线程停止
线程一般为死循环,但是我们如果想暂停线程咋办,可以使用下面方式。
创建一个线程thread1是个死循环。当全局变量thread_is_run为真时1s打印一次运行,为假时打印停止。
线程thread2用于控制thread_is_run,间接控制了thread1暂停和运行.
注意:我们这里使用time.sleep()来模拟线程1停止,实际上线程1还在运行,只是打印变慢。python应该也提供了线程挂起的接口把。先不关注
示例:
#!/usr/bin/python
import threading
import time
class MyThread(threading.Thread):
def __init__(self, target, name):
super(MyThread, self).__init__(name=name)
self.target = target
# 重写threading.Thread.start()接口
def run(self):
self.target()
thread_is_run = True
def thread1_entry():
global thread_is_run
cur_thread = threading.current_thread()
while True:
if thread_is_run:
print("cur_thread name:{0} run...".format(cur_thread.name))
time.sleep(1)
else:
print("cur_thread name:{0} stoped".format(cur_thread.name))
time.sleep(500)
print("cur_thread name:{0} is exit".format(cur_thread.name))
def thread2_entry():
cur_thread = threading.current_thread()
while True:
global thread_is_run
cmd = input()
if cmd == "1":
thread_is_run = True
print("input:{0}".format(cmd))
else:
thread_is_run = False
print("input:{0}".format(cmd))
print("cur_thread name:{0} is exit".format(cur_thread.name))
def main():
thread1 = MyThread(thread1_entry, "thread-1")
thread1.start()
thread2 = MyThread(thread2_entry, "thread-2")
thread2.start()
if __name__ == "__main__":
main()
6- 线程锁 threading.Lock()
对于多线程问题,经常存在公共资源(临界资源)同时被多个线程访问、修改的问题,如果不能保证多线程互斥地访问临界资源,就存在逻辑上的问题。操作系统一般提供互斥锁、原子锁等。python了解的不多,只知道线程锁threading.lock。
python中的线程锁:当一个线程获取到锁后,其他线程就会阻塞,知道该线程释放锁。
示例:
示例中以停车场为背景,停车场共5个车位,初始情况下有还剩3个车位。创建两个线程,一个用于进入停车场停车,一个用于离开停车场。
如果不加锁,可能存在如下情况:
本来停车场还有3个车位,同一时刻A离开停车场,B进入停车场。他们被告知此时还有3个车位。
A离开的快,离开后车位还有3+1=4个
B停车慢,停好车后车位还有3-1=2个
加了锁后就不会出现这种情况。加锁后就相当于只有一个进出口,同一时刻只能进或只能出,这样操作临界资源(停车位数目)就安全了。
#!/usr/bin/python
import random
import threading
import time
class Parking(threading.Thread):
def __init__(self, target, name):
super(Parking, self).__init__(name=name)
self.target = target
# 重写threading.Thread.start()接口
def run(self):
self.target()
lock = threading.Lock()
park_cnt = 3
def drive_in():
lock.acquire()
global park_cnt
if park_cnt == 0:
print("sorry, you can't drive in, because there is not parking")
elif park_cnt <= 5:
park_cnt -= 1
print("drive in, par_cnt:{0}".format(park_cnt))
lock.release()
time.sleep(1)
def drive_out():
lock.acquire()
global park_cnt
if park_cnt == 5:
print("sorry, you maybe crazy, there is no car in parking")
elif park_cnt < 5:
park_cnt += 1
print("drive out, par_cnt:{0}".format(park_cnt))
lock.release()
time.sleep(1)
def drive_in_thread():
cur_thread = threading.current_thread()
while True:
drive_in()
print("cur_thread name:{0} is exit".format(cur_thread.name))
def drive_out_thread():
cur_thread = threading.current_thread()
while True:
drive_out()
print("cur_thread name:{0} is exit".format(cur_thread.name))
def main():
drive_in = Parking(drive_in_thread, "drive_in")
drive_in.start()
drive_out = Parking(drive_out_thread, "drive_out")
drive_out.start()
if __name__ == "__main__":
main()
7- 线程通知 threading.Condition()
acquire([timeout])/release(): 调用关联的锁的相应方法。
wait([timeout]): 调用这个方法将使线程进入Condition的等待池等待通知,并释放锁。使用前线程必须已获得锁定,否则将抛出异常。
notify(): 调用这个方法将从等待池挑选一个线程并通知,收到通知的线程将自动调用acquire()尝试获得锁定(进入锁定池);其他线程仍然在等待池中。调用这个方法不会释放锁定。使用前线程必须已获得锁定,否则将抛出异常。
notifyAll(): 调用这个方法将通知等待池中所有的线程,这些线程都将进入锁定池尝试获得锁定。调用这个方法不会释放锁定。使用前线程必须已获得锁定,否则将抛出异常。
注意:调用condition.wait()时会将线程加入到等待池中,并释放锁!释放锁。这一点没看帖子前一直不知道还有这样的操作。。。
参考:https://www.cnblogs.com/zj1111184556/p/4612470.html
示例:
还是以停车场为例,停车场共5个车位。初始情况下还剩3个车位。
有车驶入,车位--
有车驶出,车位++
如果停车场满了,车无法进入,等待。当有车驶出,通知车主可以停车了
当停车场空了,无车可驶出,等待。当有车驶入,通知可以驶出。
下面的例子只是说明condition用法,并没有很好的保护临界资源(停车位数量)
#!/usr/bin/python
import random
import threading
import time
class Parking(threading.Thread):
def __init__(self, target, name):
super(Parking, self).__init__(name=name)
self.target = target
# 重写threading.Thread.start()接口
def run(self):
self.target()
condition = threading.Condition()
park_cnt = 3
def drive_in():
global condition
condition.acquire()
global park_cnt
#没有停车位,等待阻塞
if park_cnt == 0:
print("sorry, you can't drive in, because there is not parking")
condition.wait()
#有停车位,将车辆驶入停车场。现在停车场有车,所以通知车可以驶出停车场
park_cnt -= 1
print("drive in, par_cnt:{0}".format(park_cnt))
condition.notify()
condition.release()
#sleeptime = random.randint(1, 5)
#time.sleep(sleeptime)
time.sleep(1)
def drive_out():
global condition
condition.acquire()
global park_cnt
#停车场没有车,不可驶出,阻塞等待有车才驶出
if park_cnt == 5:
print("sorry, there is no car in parking")
condition.wait()
#停车场有车,可以驶出。驶出后空出车位,所以通知车可以停入
park_cnt += 1
print("drive out, par_cnt:{0}".format(park_cnt))
condition.notify()
condition.release()
sleeptime = random.randint(1, 5)
time.sleep(sleeptime)
def drive_in_thread():
cur_thread = threading.current_thread()
while True:
drive_in()
print("cur_thread name:{0} is exit".format(cur_thread.name))
def drive_out_thread():
cur_thread = threading.current_thread()
while True:
drive_out()
print("cur_thread name:{0} is exit".format(cur_thread.name))
def main():
drive_in = Parking(drive_in_thread, "drive_in")
drive_in.start()
drive_out = Parking(drive_out_thread, "drive_out")
drive_out.start()
if __name__ == "__main__":
main()
8- 线程通知threading.Event()