当前位置:   article > 正文

python 多线程与线程池_python的线程池和 thread有什么区别

python的线程池和 thread有什么区别

线程:
线程(Thread)也叫轻量级进程,是操作系统能够进行运算调度的最小单位,它被包涵在进程之中,是进程中的实际运作单位。线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行

并发与并行:
单核CUP执行多任务,由于CUP执行代码都是顺序执行的,当执行多个任务的时候操作系统会轮流让各个任务交替执行,由于执行速度过快,感觉是所有的任务同时执行一样,称为并发,当任务数小于CUP数量时,这是任务是真正的在一起运行,称为并行。

同步与异步:
同步(协同步调):线程在访问某 一资源时,获得了资源的返回结果后才会执行其他操作。
异步(步调各异):与同步相对,在访问某一资源时,无论资源是否有返回结果,都会进行下一步操作,当资源有结果时,系统会自动通知线程。

举例:创建两个普通函数

import time

def fun():
    for i in range(3):
        time.sleep(1)
        print('fun:', i)


def fun1():
    for i in range(3):
        time.sleep(1)
        print('fun1:', i)

st = time.time()
fun()
fun1()
et = time.time()
print("运行时间:", et - st)


运行结果:
fun: 0
fun: 1
fun: 2
fun1: 0
fun1: 1
fun1: 2
运行时间: 6.063438653945923
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

Threading模块
通过多线程对多任务的处理,通过threading中的Thread模块。如下通过Thread创建一个线程对象,将需要运行的函数名t赋值给target,调用对象的start方法来启动线程。

在这里插入图片描述

import time
from threading import Thread


def fun():
    for i in range(3):
        time.sleep(1)
        print('fun:', i)


def fun1():
    for i in range(3):
        time.sleep(1)
        print('fun1:', i)


st = time.time()
fun = Thread(target=fun) # 创建线程对象
fun.start() # 启动线程
fun1()
et = time.time()
print("运行时间:", et - st)
结果:
fun: 0
fun1: 0
fun1: 1
fun: 1
fun: 2
fun1: 2
运行时间: 3.0180318355560303
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

如果fun1也通过创建线程的方式来启动,来看下效果:

import time
from threading import Thread


def fun():
    for i in range(3):
        time.sleep(1)
        print('fun:', i)


def fun1():
    for i in range(3):
        time.sleep(1)
        print('fun1:', i)


st = time.time()
fun = Thread(target=fun) # 创建线程对象
fun1 = Thread(target=fun1) # 创建线程对象
fun.start() # 启动线程
fun1.start() # 启动线程
et = time.time()
print("运行时间:", et - st) # 主线程

结果:
运行时间: 0.0009970664978027344
fun: 0
fun: 1
fun: 2
fun1: 0
fun1: 1
fun1: 2
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

这时候主线程并print并没有等待子线程运行结束,现有需求需要等待子线程执行结束后才运行主线程,可以用join()方法来控制等待子线程运行的时间,默认是等待子线程运行结束。
举例:

import time
from threading import Thread


def fun():
    for i in range(3):
        time.sleep(1)
        print('fun:', i)


def fun1():
    for i in range(3):
        time.sleep(1)
        print('fun1:', i)


st = time.time()
fun = Thread(target=fun) # 创建线程对象
fun1 = Thread(target=fun1) # 创建线程对象

fun.start() # 启动线程
fun1.start() # 启动线程

fun.join() # 等待fun线程运行结束,fun.join(2) 等待2秒
et = time.time()
print("运行时间:", et-st)

结果:
fun: 0
fun1: 0
fun1: 1
fun: 1
fun: 2
fun1: 2
运行时间: 3.0180318355560303
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

任务函数有参数时
方式一:元组的形式赋值给args

import time
from threading import Thread

def fun(name,age):
    for i in range(3):
        time.sleep(1)
        print(f'fun:{name},{age}', i)


fun = Thread(target=fun, args=('MING',19))  # 创建线程对象,函数名赋值给args
fun.start()

结果:
fun:MING,19 0
fun:MING,19 1
fun:MING,19 2
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

方式二:字典的方式赋值给kwargs

import time
from threading import Thread

def fun(name,age):
    for i in range(3):
        time.sleep(1)
        print(f'fun:{name},{age}', i)


fun = Thread(target=fun, kwargs={'name': 'MING', 'age': 19})  # 创建线程对象
fun.start()

结果:
fun:MING,19 0
fun:MING,19 1
fun:MING,19 2
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

自定义线程类
重写线程类的run方法,在run方法中写线程执行的具体代码,调用start方法时会默认执行run下面的代码。

import time
from threading import Thread


class MyThread(Thread):
    def __init__(self,name):
        super().__init__() # 重写init方法
        self.name = name


    def run(self):
        for i in range(10):
            time.sleep(1)
            print(f'{self.name}请求第{i}次')


if __name__ == '__main__':
    for i in range(5):
        t = MyThread("MING")
        t.start()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

多线程共享全局变量

from threading import Thread

a = 0

def fun():
    global a
    for i in range(100):
        a += 1
    print("fun:", a)


def fun1():
    global a
    for i in range(100):
        a += 1
    print("fun1:", a)


if __name__ == '__main__':
    Thread(target=fun).start()
    Thread(target=fun1).start()
    
>>> fun: 100
>>> fun1: 200
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

多线程是无法利用多核CPU的资源,在同一时间,只能有一个线程在执行,因为python解释器有把GIL(Global Interpreter Lock)全局解释器锁。

如图:当线程想要执行,必须先拿到GIL,可以把GIL看作是“通行证”,并且在一个python进程中,GIL只有一个。拿不到通行证的线程,就不允许进入CPU执行。GIL只在cpython中才有,因为cpython调用的是c语言的原生线程,所以他不能直接操作cpu,只能利用GIL保证同一时间只能有一个线程拿到数据。在这里插入图片描述
而当共享全局变量的时候则会出现新的问题,python线程执行的切换机制:
1.线程执行遇到IO耗时操作,比如sleep,文件的输入输出,网络请求等。
2.线程执行时间达到一个阈值后
当启动多个线程同时,引用的全局变量会造成资源竞争而导致数据不准确
如下:

from threading import Thread

a = 0


def fun():
    global a
    for i in range(100000):
        a += 1
    print("fun:", a)


def fun1():
    global a
    for i in range(100000):
        a += 1
    print("fun1:", a)


if __name__ == '__main__':
    Thread(target=fun).start()
    Thread(target=fun1).start()
    print('a:',a)
    
>>> fun:100000
>>> fun1:200000
>>> a: 165424
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

互斥锁:Lock类
而解决上面的问题可以通过引入互斥锁,状态为锁定/非锁定,当某个线程更改共享数据时,先将其锁定,此时资源的状态为 “锁定” ,其他线程不能更改直到该线程释放资源,将资源的状态变为 ”非锁定“ , 其他线程才能再次锁定该资源。

l = threading.Lock() #创建锁
l.acquire() # 锁定
l.release() # 释放
  • 1
  • 2
  • 3

举例:

from threading import Thread, Lock

a = 0
lock = Lock()  # 创建锁


def fun():
    global a
    for i in range(100000):
        lock.acquire() # 上锁
        a += 1
        lock.release() # 释放锁
    print("fun:", a)


def fun1():
    global a
    for i in range(100000):
        lock.acquire() # 上锁
        a += 1
        lock.release() # 释放锁
    print("fun1:", a)


if __name__ == '__main__':
    t = Thread(target=fun)
    t.start()
    t1 = Thread(target=fun1)
    t1.start()
    t.join()
    t1.join()
    print('a:',a)

>>> fun:154229
>>> fun1:165424
>>> a: 200000
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

线程池:ThreadPoolExecutor
通过ThreadPoolExecutor 创建线程池,submit提交任务到线程池。max_workers指定线程池中的数量

import time
from concurrent.futures import ThreadPoolExecutor


def work(name):
    for i in range(5):
        time.sleep(1)
        print(f"{name}----{i}----")


with ThreadPoolExecutor(max_workers=4) as tp: # 创建线程池
    tp.submit(work, 'MING') # 往线程提交任务
    tp.submit(work, 'HOUX')
	tp.shutdown() # 等待线程池中所有的任务执行完成
    print("---主线程---")
    
运行结果:    
 
MING----0----HOUX----0----

MING----1----HOUX----1----

HOUX----2----MING----2----

MING----3----HOUX----3----

HOUX----4----MING----4----

---主线程---
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】
推荐阅读
相关标签
  

闽ICP备14008679号