python threading多线程

1、进程和线程

进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位。举个例子来说,打开任务管理器,面板中会显示多个正在执行的程序,这些程序之间是独立执行的,互不影响,这就是进程。

在当代面向线程设计的计算机结构中,进程是线程的容器,线程是进程中的最小执行单位,进程的执行依赖线程,并且一个进程中至少存在一个线程。

那么怎样理解多线程呢?其中有两个重要的概念:串行和并行。提到这个概念,第一时间容易想到物理中的电阻串联和并联,其实有相似之处。将程序要执行的任务视为电阻,那么串行就相当于任务A和任务B串联,电流只能先流过A再流过B,只能按顺序执行。

 而并行相当于任务A和任务B并联,电流可同时流过A和B,也就是说程序执行时,任务A和任务B是同时执行的。举个例子,在app听《花海》,同时还可以评论、浏览其它作品等。

 2、多线程的适用场景

 理解了上述的串行和并行的概念,自然就明晰了并行任务更适合适用多线程。常见的任务主要分为计算密集型和I/O密集型任务,由于计算密集型任务的执行是有逻辑顺序的,比如要计算(1+1)*2,必须先计算加法结果得到2,再计算乘法得到最终的结果4。那么这种场景下,就算使用多线程,由于计算逻辑顺序的存在,也不会提升执行效率。而另一种I/O密集型任务,单个任务的执行之间互不影响,没有逻辑依赖性,适合多线程的应用场景。比如多个文件的下载,多个文件可同时下载,下载效率会大大提升。

3、多线程的使用

python中提供了threading模块以供使用创建多线程程序。码!MyThread继承Thread类,并重写了run方法为一个倒计时过程。

from threading import Thread, Lock
import time


class MyThread(Thread):

    def __init__(self, task):
        super(MyThread, self).__init__()
        self.task = task

    def run(self) -> None:
        print('task', self.task)
        print('2s')
        time.sleep(1)
        print('1s')
        time.sleep(1)
        print('0s')
        time.sleep(1)
        print('sub-thread terminated!')

(1) 创建并开启多线程:

t1 = MyThread('t1')
t2 = MyThread('t2')
t1.start()
t2.start()

如果打断点进行debug,会发现前两行创建线程并初始化,后两行执行run方法,debug可以控制线程的执行。执行结果:任务t1和任务t2几乎是同时执行的。

 (2) 线程守护

一个执行程序中,创建的多线程为子线程,还有一个主线程,负责整个程序的执行,线程守护是为了协调主线程和子线程之间的关系。如下创建线程t3,设置为守护线程。

t3 = MyThread('t3')
t3.setDaemon(True)
t3.start()

print('main thread terminated!')

 执行结果中,主线程结束时,子线程也全部结束,无论是否执行完毕。

 如下创建线程t4,设置为守护线程,并添加到线程等待中。

t4 = MyThread('t4')
t4.setDaemon(True)
t4.start()
t4.join()
print('main thread terminated!')

 执行中,主线程会等待所有子线程执行完毕之后再结束。

(3)线程安全

 在使用多线程时,线程之间存在数据共享,例如多线程共享全局变量。加入一个线程在从前往后的读取一个元素全为0的列表,而另一个线程同时在从后往前修改列表的元素为1。这样线程一本该输出全为0,但因为线程二对数据的修改,输出了不同的数据,这就造成了数据安全问题,类似于sql数据库中的dirty read。样例:声明一个全局变量k,初始值为0,任务1负责对k进行加1修改,任务2负责打印k,采用普通的方式创建两个线程并开启,输出结果均为3。

K = 0


def task1():
    global K
    for i in range(3):
        K += 1
    print('task1 k: %d' % K)


def task2():
    global K
    print('task2 k: %d' % K)


## 多线程共享全局变量
t5 = Thread(target=task1)
t5.start()
t6 = Thread(target=task2)
t6.start()

 要解决线程的安全问题,需要借助线程锁来对线程进行管理。线程锁能够将一个线程锁定,其它线程只能等到该线程执行完毕之后再继续执行。代码中新建一个任务3,带有参数线程锁,并对全局变量修改后进行加锁。main函数中创建了线程锁,并创建了20个线程添加到线程等待。

N = 100

def task3(lock):
    global N
    lock.acquire(blocking=True, timeout=-1)
    for i in range(10):
        N -= 1
        print(N)
    lock.release()


def task4():
    global N
    for i in range(5):
        N += 10
        print('*******'*4, N)


def main():
    lock = Lock()
    tp = []
    t7 = Thread(target=task3, args=(lock, ))
    t8 = Thread(target=task4)
    t7.start()
    t8.start()

不加锁执行结果:

 加锁执行结果:线程t7执行完之后,线程t8执行。

 

 

 才疏学浅,欢迎指正!

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值