文章目录
- 什么是线程
- 什么是线程
- 为什么使用线程
- 线程的创建与使用
- 使用 Thread 类的构造函数
- 从Thread类继承并覆盖run()函数
- 线程锁
- 什么是线程锁
- 死锁
- 解决死锁
- 线程通信
- 多进程
什么是线程
什么是线程
为什么使用线程
- 使用线程可以把占据长时间的任务放到后台去处理,防止页面卡顿。
- 在多核CPU系统中,使用线程可以提高程序响应速度,提高CPU和内存的利用率。
- 在并发操作时使用多线程,如C/S架构的服务器端并发线程响应用户的请求。
- 改善程序结构,会利于理解和修改。
线程的创建与使用
在Python3中,线程的操作借助threading
模块。
import threading
创建线程一般使用threading
中的Thread
类,有两种使用方案。
- 直接传入要运行的方法
- 从
Thread
类继承并覆盖run()
函数。
使用 Thread 类的构造函数
Thread(group=None, target=None,
name=None, args=(), kwargs={})
import threading
def sub_thread(arg):
print("sub thread %s starting..."%threading.currentThread().getName())
print('the arg is %s.'%arg)
print('sub thread end.')
def main():
print('main thread starting...')
t = threading.Thread(target = sub_thread, name = 'sub_t', args=(100, ))
t.start()
print('main thread end.')
main()
import threading
import time
def sub_thread(arg):
print('sub thread starting...')
count = 1
while count<10:
time.sleep(0.3)
print("I'm sub thread and working %s times"%count)
count += 1
print('sub thread end.')
t = threading.Thread(target=sub_thread, args=(1, ))
t.start()
print('main thread starting...')
count = 1
while count<10:
time.sleep(0.6)
print("I'm main thread and working %s times"%count)
count += 1
print('main thread end.')
从Thread类继承并覆盖run()函数
import threading
class MyThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
print('sub thread %s starting...'%threading.currentThread().getName())
print('sub thread end.')
def main():
print('main thread strating...')
t = MyThread()
t.start()
print('main thread end.')
main()
import threading
import time
class MyThread(threading.Thread):
def __init__(self, name):
threading.Thread.__init__(self)
self.name = name
def run(self):
print('sub thread starting...')
count = 1
while count<10:
time.sleep(0.3)
print("I'm %s and working %s times"%(self.name, count))
count += 1
print('sub thread end.')
t = MyThread('sub Thread1')
t.start()
print('main thread starting...')
count = 1
while count<10:
time.sleep(0.6)
print("I'm main thread and working %s times"%count)
count += 1
print('main thread end.')
线程锁
什么是线程锁
多线程可以提高整个程序的运行效率,但在多个线程同时访问一个公共变量或公共资源就存在问题。
import threading
import time
count = 0
def sub_thread():
global count # 声明全局变量
count += 1
time.sleep(0.2)
print('Some thread make one, total is %d.\n'%count)
for i in range(5):
t = threading.Thread(target = sub_thread)
t.start()
第一次运行
第二次运行
两次操作的结果不一样(换行),而且无规律(本来应该输出 1 2 3 4 5)
由于线程之间随机调度,某线程可能在执行n条后,CPU接着执行其它线程。为了多个线程同时操作一个变量或资源时不产生混乱,我们要使用线程锁。
import threading
import time
count = 0
lock = threading.Lock()
def sub_thread():
lock.acquire()
global count # 声明全局变量
count += 1
time.sleep(0.2)
print('Some thread make one, total is %d.\n'%count)
lock.release()
for i in range(5):
t = threading.Thread(target = sub_thread)
t.start()
死锁
当程序中有多个变量或资源需要用锁保护时,可能需要用多个锁。但这时可能引起死锁问题。
import threading
import time
lockA = threading.Lock()
lockB = threading.Lock()
a,b = 1,1
def func_1():
global a,b
lockA.acquire()
a += 1
print('in func_1, a is %d.'%(a))
lockB.acquire()
b += 1
print('in func_1, b is %d.'%(b))
lockB.release()
lockA.release()
def func_2():
global a,b
lockB.acquire()
b -= 1
print('in func_2, b is %d.'%(b))
lockA.acquire()
a -= 1
print('in func_2, a is %d.'%(a))
lockB.release()
lockA.release()
t1 = threading.Thread(target=func_1, args=())
t2 = threading.Thread(target=func_2, args=())
t1.start()
t2.start()
这里就发生了死锁,第一个线程锁了a
,输出了a
,然后第二个线程锁了b
,输出了b
,这时线程要访问b
,但b
被线程2锁了,所以线程1阻塞了,线程2要访问a
,但a
被线程1锁了,所以线程2阻塞了,这时就发生了死锁。
**在两个或多个任务中,如果每个任务锁定了其它任务试图锁定的资源,此时会造成这些任务永久阻塞,从而出现死锁。
解决死锁
Python中提供了递归锁Rlock
,又称可重入锁,可以在线程需要访问多个变量或资源时使用,避免死锁发生。
import threading
import time
rlock = threading.RLock()
a,b = 1,1
def func_1():
global a,b
rlock.acquire()
a += 1
print('in func_1, a is %d.'%(a))
rlock.acquire()
b += 1
print('in func_1, b is %d.'%(b))
rlock.release()
rlock.release()
def func_2():
global a,b
rlock.acquire()
b -= 1
print('in func_2, b is %d.'%(b))
rlock.acquire()
a -= 1
print('in func_2, a is %d.'%(a))
rlock.release()
rlock.release()
t1 = threading.Thread(target=func_1, args=())
t2 = threading.Thread(target=func_2, args=())
t1.start()
t2.start()
线程通信
在同一进程内,多个线程之间有时需要同步一些信息,需要进行通信。在python中,线程之间通信常用的是 Queue
模块。
import threading
import time
import queue
mes_que = queue.Queue(maxsize = 10)
def backgroundthread(count):
sum = 0
for i in range(1, count + 1):
time.sleep(0.1)
if i%10==0:
mes_que.put("{por}".format(por=i//10))
sum += i
mes_que.put("total sum is {x}".format(x=sum))
def forgroundthread():
while 1:
got = mes_que.get()
print(got)
if got[:5] == 'total':
break
print('Done')
t1 = threading.Thread(target=backgroundthread, args = (100, ))
t2 = threading.Thread(target=forgroundthread)
t1.start()
t2.start()
多进程
在Python中,由于GIL(Global Interpreter Lock)的存在,当每个线程要使用CPU的时候,都会受这个锁的限制,所以很难达到真正的多任务同时运行,更无法利用现代CPU多核的能力。
而且,多线程的debug操作起来更麻烦,其复杂程度会浪费很大精力。
所以在Python中,要分散工作负载,使用多进程会更简单有效。