多线程的理解和学习

最近看了一些多线程的东西,慢慢整理如下

一、多线程

首先什么是多线程?通俗点讲,我们常见的看视屏啊、word写文档,内部都是多线程的模式执行的,因为看视屏需要有图像和声音,你不可能先看完图像再听声音,反之也不可以;word写文档时,你既要写文档,又要实时检查错误和保存,也是一个多线程的过程。把分任务分成每一个的线程,然后依次分时间的执行,比如A任务执行0.01秒,B任务执行0.02秒,由于CPU的调度执行速度太快了,我们基本看不出先后,所以我们感觉这个任务就跟同时执行一样。

二、python语法

Python的标准库提供了两个模块:_threadthreading_thread是低级模块,threading是高级模块,对_thread进行了封装。绝大多数情况下,我们只需要使用threading这个高级模块。

1、启动一个线程就是把一个函数传入并创建Thread实例,然后调用start()开始执行。我们创建一个线程执行的函数,然后启动两个线程执行:

# -*- coding: utf-8 -*-
"""
使用threading实现多线程
"""
import time, threading

# 新线程执行的代码
def loop(max):
    print("thread %s is running..." % (threading.current_thread().name))
    n = 0
    while n < max:
        n += 1
        print("thread %s >>> %s" % (threading.current_thread().name, n))
        time.sleep(1)
    print("thread %s ended" % (threading.current_thread().name))

def threading_1():
    # 进程进来默认启动一个主线程
    print("thread %s is running..." % (threading.current_thread().name))
    # 启动两个线程执行,观察基本同时执行的过程
    t_example1 = threading.Thread(target=loop, args=(5,), name="loopThread1")
    t_example2 = threading.Thread(target=loop, args=(10,), name="loopThread2")
    t_example1.start()
    t_example2.start()
    t_example1.join()
    t_example2.join()
    print("thread %s ended" % (threading.current_thread().name))


if __name__ == '__main__':
    threading_1()

两个线程执行的结果

thread MainThread is running...
thread loopThread1 is running...
thread loopThread1 >>> 1
thread loopThread2 is running...
thread loopThread2 >>> 1
thread loopThread2 >>> 2
thread loopThread1 >>> 2
thread loopThread1 >>> 3
thread loopThread2 >>> 3
thread loopThread2 >>> 4
thread loopThread1 >>> 4
thread loopThread2 >>> 5
thread loopThread1 >>> 5
thread loopThread2 >>> 6
thread loopThread1 ended
thread loopThread2 >>> 7
thread loopThread2 >>> 8
thread loopThread2 >>> 9
thread loopThread2 >>> 10
thread loopThread2 ended
thread MainThread ended

2、比较两个线程执行和两个进程执行的时间

# 比较两个进程和两个线程执行的时间
def compare_time():
    start1 = time.time()
    threading_1()
    print("threading time:",time.time()-start1)

    start2 = time.time()
    loop(5)
    loop(10)
    print("processing time:", time.time()-start2)

if __name__ == '__main__':
    #threading_1()
    compare_time()

上述程序执行结果:

thread MainThread is running...
thread loopThread1 is running...
thread loopThread1 >>> 1
thread loopThread2 is running...
thread loopThread2 >>> 1
thread loopThread2 >>> 2
thread loopThread1 >>> 2
thread loopThread1 >>> 3
thread loopThread2 >>> 3
thread loopThread2 >>> 4
thread loopThread1 >>> 4
thread loopThread2 >>> 5
thread loopThread1 >>> 5
thread loopThread2 >>> 6
thread loopThread1 ended
thread loopThread2 >>> 7
thread loopThread2 >>> 8
thread loopThread2 >>> 9
thread loopThread2 >>> 10
thread loopThread2 ended
thread MainThread ended
threading time: 10.00826644897461
thread MainThread is running...
thread MainThread >>> 1
thread MainThread >>> 2
thread MainThread >>> 3
thread MainThread >>> 4
thread MainThread >>> 5
thread MainThread ended
thread MainThread is running...
thread MainThread >>> 1
thread MainThread >>> 2
thread MainThread >>> 3
thread MainThread >>> 4
thread MainThread >>> 5
thread MainThread >>> 6
thread MainThread >>> 7
thread MainThread >>> 8
thread MainThread >>> 9
thread MainThread >>> 10
thread MainThread ended
processing time: 15.011106252670288

执行结果可以看出:多线程执行的过程基本是同时的,且比顺序执行这几个进程执行速度快。

3、加锁机制:当多个线程同时修改一个数据时,容易造成把数据改乱。所以需要把每个线程加锁,使该线程执行时,不被其他线程干扰。

我们定义了一个共享变量balance,初始值为0,并且启动两个线程,先存后取,理论上结果应该为0,但是,由于线程的调度是由操作系统决定的,当t1、t2交替执行时,只要循环次数足够多,balance的结果就不一定是0了。

# -*- coding: utf-8 -*-
"""
【多线程加锁机制】
多个线程同时改一个变量,容易把内容给改乱了
"""
import time, threading

# 假定这是银行存款
balance = 0

def change_it(n):
    global balance
    # 先存
    balance += n
    # 再取
    balance -= n

# 线程运行
def run_thread(n):
    for i in range(1000000):
        change_it(n)

# 开启两个线程执行
def threading_1():
    t1 = threading.Thread(target=run_thread, args=(5,))
    t2 = threading.Thread(target=run_thread, args=(8,))
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print(balance)

if __name__ == '__main__':
    threading_1()

执行结果:16(不同执行情况不同)

执行时,只要循环次数足够多,balance的结果就不一定是0了。

原因是因为高级语言的一条语句在CPU执行时是若干条语句,即使一个简单的计算:

balance = balance + n

也分两步:

  1. 计算balance + n,存入临时变量中;
  2. 将临时变量的值赋给balance

也就是可以看成:

x = balance + n
balance = x

究其原因,是因为修改balance需要多条语句,而执行这几条语句时,线程可能中断,从而导致多个线程把同一个对象的内容改乱了。

两个线程同时一存一取,就可能导致余额不对,你肯定不希望你的银行存款莫名其妙地变成了负数,所以,我们必须确保一个线程在修改balance的时候,别的线程一定不能改。

如果我们要确保balance计算正确,就要给change_it()上一把锁,当某个线程开始执行change_it()时,我们说,该线程因为获得了锁,因此其他线程不能同时执行change_it(),只能等待,直到锁被释放后,获得该锁以后才能改。由于锁只有一个,无论多少线程,同一时刻最多只有一个线程持有该锁,所以,不会造成修改的冲突。创建一个锁就是通过threading.Lock()来实现:

# -*- coding: utf-8 -*-
"""
【多线程加锁机制】
多个线程同时改一个变量,容易把内容给改乱了
"""
import time, threading

# 假定这是银行存款
balance = 0

def change_it(n):
    global balance
    # 先存
    balance += n
    # 再取
    balance -= n

# 加锁线程执行
lock = threading.Lock()
def run_thread_lock(n):
    for i in range(1000000):
        # 获取锁
        lock.acquire()
        try:
            # 放心修改
            change_it(n)
        finally:
            # 每次修改完都要释放锁,这样确保可以释放锁
            lock.release()

# 开启两个线程执行,加锁
def threading_2():
    t1 = threading.Thread(target=run_thread_lock, args=(5,))
    t2 = threading.Thread(target=run_thread_lock, args=(8,))
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print(balance)


if __name__ == '__main__':
    threading_2()

执行结果永远为0

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值