一、使用threading模块实现线程的创建
实例1
import threading
from time import ctime, sleep
def target():
print("The current threading {}---{} is running".format(threading.current_thread().name, ctime()))
sleep(5)
print("The current threading {}---{} is running".format(threading.current_thread().name, ctime()))
if __name__ == "__main__":
print("The current threading {}---{} is running".format(threading.current_thread().name, ctime()))
t = threading.Thread(target=target)
t.start()
print("The current threading {}---{} is running".format(threading.current_thread().name, ctime()))
输出结果:
The current threading MainThread---Wed Oct 14 18:32:19 2020 is running
The current threading Thread-1---Wed Oct 14 18:32:19 2020 is running
The current threading MainThread---Wed Oct 14 18:32:19 2020 is running
The current threading Thread-1---Wed Oct 14 18:32:24 2020 is running
Process finished with exit code 0
import threading
首先导入threading 模块,这是使用多线程的前提。
t = threading.Thread(target=target)
创建线程t,使用threading.Thread()方法。
t.start()
开始线程活动。
使用threading.current_thread()可以查看到当前线程的信息。
从输出结果可以看到在线程Thread-1结束前MainThread已经结束了,但并没有杀死子线程Thread-1。
实例2
import threading
from time import ctime, sleep
def target():
print("The current threading {}---{} is running".format(threading.current_thread().name, ctime()))
sleep(5)
print("The current threading {}---{} is running".format(threading.current_thread().name, ctime()))
if __name__ == "__main__":
print("The current threading {}---{} is running".format(threading.current_thread().name, ctime()))
t = threading.Thread(target=target)
t.start()
t.join()
print("The current threading {}---{} is running".format(threading.current_thread().name, ctime()))
t.join()
join()的作用是,在子线程完成运行之前,这个子线程的父线程将一直被阻塞。Python中,默认情况下,如果不加join()语句,那么主线程不会等到当前线程结束才结束,但却不会立即杀死该线程。如上面的输出结果所示。
输出结果:
The current threading MainThread---Wed Oct 14 18:40:42 2020 is running
The current threading Thread-1---Wed Oct 14 18:40:42 2020 is running
The current threading Thread-1---Wed Oct 14 18:40:47 2020 is running
The current threading MainThread---Wed Oct 14 18:40:47 2020 is running
Process finished with exit code 0
实例3
import threading
from time import ctime, sleep
def target():
print("The current threading {}---{} is running".format(threading.current_thread().name, ctime()))
sleep(5)
print("The current threading {}---{} is running".format(threading.current_thread().name, ctime()))
if __name__ == "__main__":
print("The current threading {}---{} is running".format(threading.current_thread().name, ctime()))
t = threading.Thread(target=target)
t.setDaemon(True)
t.start()
t.join()
print("The current threading {}---{} is running".format(threading.current_thread().name, ctime()))
输出结果:
The current threading MainThread---Thu Oct 15 13:07:04 2020 is running
The current threading Thread-1---Thu Oct 15 13:07:04 2020 is running
The current threading Thread-1---Thu Oct 15 13:07:09 2020 is running
The current threading MainThread---Thu Oct 15 13:07:09 2020 is running
Process finished with exit code 0
t.setDaemon(True)
t.setDaemon(True)将线程声明为守护线程,必须在start() 方法调用之前设置,如果不设置为守护线程程序会被无限挂起。如果当前python线程是守护线程,那么意味着这个线程是“不重要”的,“不重要”意味着如果他的主进程结束了但该守护线程没有运行完,守护进程就会被强制结束。如果线程是非守护线程,那么父进程只有等到守护线程运行完毕后才能结束。
import threading
from time import ctime, sleep
def target():
print("The current threading {}---{} is running".format(threading.current_thread().name, ctime()))
sleep(5)
print("The current threading {}---{} is running".format(threading.current_thread().name, ctime()))
if __name__ == "__main__":
print("The current threading {}---{} is running".format(threading.current_thread().name, ctime()))
t = threading.Thread(target=target)
t.setDaemon(True)
t.start()
# t.join()
print("The current threading {}---{} is running".format(threading.current_thread().name, ctime()))
输出结果:
The current threading MainThread---Thu Oct 15 13:08:38 2020 is running
The current threading Thread-1---Thu Oct 15 13:08:38 2020 is running
The current threading MainThread---Thu Oct 15 13:08:38 2020 is running
Process finished with exit code 0
如果为线程实例添加t.setDaemon(True)之后,如果不加join语句,那么当主线程结束之后,会杀死子线程。
二、使用threading模块实现多线程的创建
1、函数的方式创建
import threading
from time import ctime, sleep
def code():
print("I'm coding. {}---{}".format(ctime(), threading.current_thread().name))
sleep(5)
def draw():
print("I'm drawing. {}---{}".format(ctime(), threading.current_thread().name))
sleep(5)
if __name__ == "__main__":
threads = []
print("The current threading {}---{} is running".format(threading.current_thread().name, ctime()))
t1 = threading.Thread(target=code)
threads.append(t1)
t2 = threading.Thread(target=draw)
threads.append(t2)
for t in threads:
t.setDaemon(True)
t.start()
t.join()
print("The current threading {}---{} is running".format(threading.current_thread().name, ctime()))
输出结果:
The current threading MainThread---Thu Oct 15 13:35:06 2020 is running
I'm coding. Thu Oct 15 13:35:06 2020---Thread-1
I'm drawing. Thu Oct 15 13:35:06 2020---Thread-2
The current threading MainThread---Thu Oct 15 13:35:11 2020 is running
Process finished with exit code 0
给线程传递参数
import threading
from time import ctime, sleep
def code(arg):
print("I'm coding.{}---{}---{}".format(arg, ctime(), threading.current_thread().name))
sleep(5)
def draw(arg):
print("I'm drawing.{}----{}---{}".format(arg, ctime(), threading.current_thread().name))
sleep(5)
if __name__ == "__main__":
threads = []
print("The current threading {}---{} is running".format(threading.current_thread().name, ctime()))
t1 = threading.Thread(target=code, args=('敲代码',))
threads.append(t1)
t2 = threading.Thread(target=draw, args=('画画',))
threads.append(t2)
for t in threads:
t.setDaemon(True)
t.start()
t.join()
print("The current threading {}---{} is running".format(threading.current_thread().name, ctime()))
输出结果:
The current threading MainThread---Thu Oct 15 13:39:49 2020 is running
I'm coding.敲代码---Thu Oct 15 13:39:49 2020---Thread-1
I'm drawing.画画----Thu Oct 15 13:39:49 2020---Thread-2
The current threading MainThread---Thu Oct 15 13:39:54 2020 is running
Process finished with exit code 0
2.类的方式创建线程
继承自threading.Thread类
为了让线程代码更好的封装,可以使用threading模块的下的Thread类,继承自这个类,然后实现run方法,线程就会自动运行run方法中的代码。
import threading
from time import ctime, sleep
class CodingThread(threading.Thread):
def run(self):
print("I'm coding.{}---{}".format(ctime(), threading.current_thread().name))
sleep(5)
class DrawingThread(threading.Thread):
def run(self):
print("I'm drawing.{}---{}".format(ctime(), threading.current_thread().name))
sleep(5)
def multi_thread():
t1 = CodingThread()
t2 = DrawingThread()
print(threading.enumerate())
t1.start()
print(threading.enumerate())
t2.start()
print(threading.enumerate())
if __name__ == "__main__":
multi_thread()
输出结果
[<_MainThread(MainThread, started 4403457344)>]
I'm coding.Thu Oct 15 13:45:06 2020---Thread-1
[<_MainThread(MainThread, started 4403457344)>, <CodingThread(Thread-1, started 123145444630528)>]
I'm drawing.Thu Oct 15 13:45:06 2020---Thread-2
[<_MainThread(MainThread, started 4403457344)>, <CodingThread(Thread-1, started 123145444630528)>, <DrawingThread(Thread-2, started 123145461420032)>]
Process finished with exit code 0
三、多线程共享全局变量以及锁机制
1、多线程共享变量的问题
对于多线程来说,最大的特点就是线程之间可以共享数据,线程的执行又是无序的,那么共享数据就会出现多线程同时更改一个变量,使用同样的资源,而出现死锁、数据错乱等情况。
import threading
value = 0
class AddValueThread(threading.Thread):
def run(self):
global value
for x in range(1000000):
value += 1
print("{}的值是{}".format(threading.current_thread().name, value))
def multi_thread():
for i in range(2):
t = AddValueThread()
t.start()
if __name__ == "__main__":
multi_thread()
输出结果:
Thread-1的值是1214452
Thread-2的值是1393110
Process finished with exit code 0
这个结果是错误的,正确的结果应该是:
Thread-1的值是1000000
Thread-2的值是2000000
由于两条线程同时对value操作,所以这里就出现数据错误了
2、线程锁和ThreadLocal
(1)线程锁
为了解决以上使用共享变量的问题。threading提供了一个Lock类,这个类可以在某个线程访问某个变量的时候加锁,其他线程就进不来,直到当前进程处理完成后,释放了锁,其他线程才能进来进行处理。当访问某个资源之前,用Lock.acquire()锁住资源,访问之后,用Lock.release()释放资源。
import threading
value = 0
gLock = threading.Lock()
class AddValueThread(threading.Thread):
def run(self):
global value
gLock.acquire()
for x in range(1000000):
value += 1
gLock.release()
print("{}的值是{}".format(threading.current_thread().name, value))
def multi_thread():
for i in range(2):
t = AddValueThread()
t.start()
if __name__ == "__main__":
multi_thread()
输出结果:
Thread-1的值是1000000
Thread-2的值是2000000
Process finished with exit code 0
(2)、ThreadLocal
介绍完线程锁,接下来出场的是ThreadLocal。当不想将变量共享给其他线程时,可以使用局部变量,但在函数中定义局部变量会使得在函数之间传递特别麻烦。ThreadLocal是非常牛逼的东西,它解决了全局变量需要枷锁,局部变量传递麻烦的两个问题。通过在线程中定义:
local_school = threading.local()
此时这个local_school就变成了一个全局变量,但这个全局变量只在该线程中为全局变量,对于其他线程来说是局部变量,别的线程不可更改。
def process_thread(name): # 绑定ThreadLocal的student:
local_school.student = name
这个student属性只有本线程可以修改,别的线程不可以。代码:
import threading
value = 0
gLocal = threading.local()
class AddValueThread(threading.Thread):
def run(self):
gLocal.value = 0
for x in range(1000000):
gLocal.value += 1
print("{}的值是{}".format(threading.current_thread().name, gLocal.value))
def multi_thread():
for i in range(2):
t = AddValueThread()
t.start()
if __name__ == "__main__":
multi_thread()
输出结果:
Thread-1的值是1000000
Thread-2的值是1000000
Process finished with exit code 0
四、生产者和消费者模式
(1)、Lock版
生产者线程专门用来生产一些数据,然后存放到中间变量中,消费者再从中间的变量中取出数据进行消费。中间变量经常是一些全局变量,所以需要使用锁来保证数据完整性。
import threading
import random
import time
gMoney = 1000
gTimes = 0
gLock = threading.Lock()
class Producer(threading.Thread):
def run(self):
global gMoney
global gTimes
while True:
money = random.randint(100, 1000)
gLock.acquire()
if gTimes >= 3:
gLock.release()
break
gMoney += money
print("{}当前存入{}元钱,剩余{}元钱".format(threading.current_thread(), money, gMoney))
gTimes += 1
time.sleep(0.5)
gLock.release()
class Consumer(threading.Thread):
def run(self):
global gMoney
global gTimes
while True:
money = random.randint(100, 500)
gLock.acquire()
if gMoney > money:
gMoney -= money
print("{}当前取出{}元钱,剩余{}元钱".format(threading.current_thread(), money, gMoney))
time.sleep(0.5)
else:
if gTimes >= 3:
gLock.release()
break
print("{}当前想取出{}元钱,剩余{}元钱,不足!".format(threading.current_thread(), money, gMoney))
gLock.release()
def multi_thread():
for i in range(2):
Consumer(name="消费者线程{}".format(i)).start()
for j in range(2):
Producer(name="生产者线程{}".format(j)).start()
if __name__ == "__main__":
multi_thread()
输出结果:
<Consumer(消费者线程0, started 123145324752896)>当前取出128元钱,剩余872元钱
<Consumer(消费者线程1, started 123145341542400)>当前取出420元钱,剩余452元钱
<Producer(生产者线程0, started 123145358331904)>当前存入997元钱,剩余1449元钱
<Producer(生产者线程1, started 123145375121408)>当前存入700元钱,剩余2149元钱
<Producer(生产者线程1, started 123145375121408)>当前存入984元钱,剩余3133元钱
<Consumer(消费者线程1, started 123145341542400)>当前取出221元钱,剩余2912元钱
<Consumer(消费者线程0, started 123145324752896)>当前取出313元钱,剩余2599元钱
<Consumer(消费者线程1, started 123145341542400)>当前取出189元钱,剩余2410元钱
<Consumer(消费者线程0, started 123145324752896)>当前取出356元钱,剩余2054元钱
<Consumer(消费者线程1, started 123145341542400)>当前取出109元钱,剩余1945元钱
<Consumer(消费者线程0, started 123145324752896)>当前取出418元钱,剩余1527元钱
<Consumer(消费者线程1, started 123145341542400)>当前取出381元钱,剩余1146元钱
<Consumer(消费者线程0, started 123145324752896)>当前取出416元钱,剩余730元钱
<Consumer(消费者线程0, started 123145324752896)>当前取出166元钱,剩余564元钱
<Consumer(消费者线程0, started 123145324752896)>当前取出111元钱,剩余453元钱
<Consumer(消费者线程1, started 123145341542400)>当前取出384元钱,剩余69元钱
<Consumer(消费者线程0, started 123145324752896)>当前想取出415元钱,剩余69元钱,不足!
<Consumer(消费者线程1, started 123145341542400)>当前想取出100元钱,剩余69元钱,不足!
Process finished with exit code 0
(2)、Condition版
LOCK版本的生产者和消费者存在一个不足,在消费者中总是通过while True死循环并且上锁的方式判断资源够不够。上锁是一个很耗费cpu资源的行为。因此这种方式不是最好的。还有一种更好的方式是使用threading.Condition来实现。threading.Condition消费者可以在没有数据的时候处于阻塞等待状态。生产者一旦有合适的数据,还可以使用notify相关的函数来通知处于等待阻塞状态的线程。这样就可以避免一些无用的上锁、解锁的操作。
threading.Condition类似threading.Lock,可以在修改全局数据的时候进行上锁,也可以在修改完毕后进行解锁。
acquire:上锁
release:解锁
wait:将当前线程处于等待状态,并且会释放锁。可以被其他线程使用notify和notify_all函数唤醒。被唤醒后继续等待上锁,上锁后继续执行下面的代码。
notify:通知某个正在等待的线程,默认是第1个等待的线程。
notify_all:通知所有正在等待的线程。
注意: notify和notify_all不会释放锁。并且需要在release之前调用。
import threading
import random
import time
gMoney = 1000
gCondition = threading.Condition() # 锁
gTimes = 0
class Producer(threading.Thread):
def run(self):
global gMoney
global gTimes
while True:
money = random.randint(100, 1000)
gCondition.acquire()
if gTimes >= 3:
gCondition.release()
break
gMoney += money
print("{}当前存入{}元钱,剩余{}元钱".format(threading.current_thread(), money, gMoney))
gTimes += 1
gCondition.notify_all()
gCondition.release()
time.sleep(0.5)
class Consumer(threading.Thread):
def run(self):
global gMoney
global gTimes
while True:
money = random.randint(100, 500)
gCondition.acquire()
while gMoney < money:
print("{}准备消费{}元钱,还剩{}元钱,余额不足!".format(threading.current_thread(), money, gMoney))
if gTimes >= 3:
gCondition.release()
return
gCondition.wait()
gMoney -= money
print("{}消费了{}元钱,剩余{}元钱".format(threading.current_thread(), money, gMoney))
gCondition.release()
time.sleep(0.5)
def multi_thread():
for i in range(2):
Consumer(name="消费者线程{}".format(i)).start()
for j in range(2):
Producer(name="生产者线程{}".format(j)).start()
if __name__ == "__main__":
multi_thread()
输出结果:
<Consumer(消费者线程0, started 123145357996032)>消费了273元钱,剩余727元钱
<Consumer(消费者线程1, started 123145374785536)>消费了470元钱,剩余257元钱
<Producer(生产者线程0, started 123145391575040)>当前存入181元钱,剩余438元钱
<Producer(生产者线程1, started 123145408364544)>当前存入464元钱,剩余902元钱
<Consumer(消费者线程0, started 123145357996032)>消费了455元钱,剩余447元钱
<Producer(生产者线程0, started 123145391575040)>当前存入677元钱,剩余1124元钱
<Consumer(消费者线程1, started 123145374785536)>消费了400元钱,剩余724元钱
<Consumer(消费者线程0, started 123145357996032)>消费了485元钱,剩余239元钱
<Consumer(消费者线程1, started 123145374785536)>消费了159元钱,剩余80元钱
<Consumer(消费者线程0, started 123145357996032)>准备消费325元钱,还剩80元钱,余额不足!
<Consumer(消费者线程1, started 123145374785536)>准备消费229元钱,还剩80元钱,余额不足!
Process finished with exit code 0
网友评论