文章目录

  • 什么是线程
  • 什么是线程
  • 为什么使用线程
  • 线程的创建与使用
  • 使用 Thread 类的构造函数
  • 从Thread类继承并覆盖run()函数
  • 线程锁
  • 什么是线程锁
  • 死锁
  • 解决死锁
  • 线程通信
  • 多进程


什么是线程

什么是线程

python如何打开多线程 python多线程教程_python如何打开多线程


为什么使用线程

  • 使用线程可以把占据长时间的任务放到后台去处理,防止页面卡顿。
  • 在多核CPU系统中,使用线程可以提高程序响应速度,提高CPU和内存的利用率。
  • 在并发操作时使用多线程,如C/S架构的服务器端并发线程响应用户的请求。
  • 改善程序结构,会利于理解和修改。

线程的创建与使用

在Python3中,线程的操作借助threading模块。

import threading

创建线程一般使用threading中的Thread类,有两种使用方案。

  • 直接传入要运行的方法
  • Thread类继承并覆盖run()函数。

使用 Thread 类的构造函数

Thread(group=None, target=None, 
	name=None, args=(), kwargs={})

python如何打开多线程 python多线程教程_python如何打开多线程_02

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

python如何打开多线程 python多线程教程_多进程_03


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.')

python如何打开多线程 python多线程教程_python_04


从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.')

python如何打开多线程 python多线程教程_python如何打开多线程_05


线程锁

什么是线程锁

多线程可以提高整个程序的运行效率,但在多个线程同时访问一个公共变量或公共资源就存在问题。

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

第一次运行

python如何打开多线程 python多线程教程_python如何打开多线程_06


第二次运行

python如何打开多线程 python多线程教程_多进程_07


两次操作的结果不一样(换行),而且无规律(本来应该输出 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()

python如何打开多线程 python多线程教程_python如何打开多线程_08


死锁

当程序中有多个变量或资源需要用锁保护时,可能需要用多个锁。但这时可能引起死锁问题。

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

python如何打开多线程 python多线程教程_多线程_09


这里就发生了死锁,第一个线程锁了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如何打开多线程 python多线程教程_python_10


线程通信

在同一进程内,多个线程之间有时需要同步一些信息,需要进行通信。在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如何打开多线程 python多线程教程_python如何打开多线程_11


多进程

在Python中,由于GIL(Global Interpreter Lock)的存在,当每个线程要使用CPU的时候,都会受这个锁的限制,所以很难达到真正的多任务同时运行,更无法利用现代CPU多核的能力。

而且,多线程的debug操作起来更麻烦,其复杂程度会浪费很大精力。

所以在Python中,要分散工作负载,使用多进程会更简单有效。

python如何打开多线程 python多线程教程_python如何打开多线程_12


python如何打开多线程 python多线程教程_多线程_13


python如何打开多线程 python多线程教程_python_14