python学习之线程

线程

什么是线程?

在一个程序中,可独立运行的程序片段叫作“线程”(Thread),利用它编程的概念就叫作“多线程处理”。多线程是为了同步完成多项任务,不是为了提高运行效率,而是为了提高资源使用效率来提高系统的效率(线程之间可以共享内存和变量,资源消耗少)。线程是在同一时间需要完成多项任务的时候实现的。

线程的优劣
- 优势:线程之间可共享内存和变量,资源消耗少
- 不足:线程之间的同步和加锁较麻烦

threading库

在python中主要是通过thread和threading两个标准块实现,thread是低级版本,threading是更高级的版本,一般来说都是使用threading这个模块,以下

基础款

一个进程有至少有一个线程,因此任何进程默认就会启动一个线程,此为主线程,主线程又可启动新的线程,Python的threading模块有个current_thread()函数,它永远返回当前线程的实例。主线程实例的名字叫MainThread,子线程的名字在创建时指定,而名字仅仅在打印时用来显示,完全没有其他意义,如果不起名字Python就自动给线程命名为Thread-1,Thread-2……

import threading

def my_print():
    print 'thread %s is running...' %threading.current_thread().name
    for i in range(3):
        print '%s >>> %d' %(threading.current_thread().name, i)

print 'thread %s is running...' %threading.current_thread().name
thread1 = threading.Thread(target=my_print, name='First')
thread1.start()
thread1.join() # 等待子线程处理结束后,再执行main代码
print 'thread %s is ending' %threading.current_thread().name

# 结果
thread MainThread is running...
thread First is running...
First >>> 0
First >>> 1
First >>> 2
thread MainThread is ending

多线程

上述代码是线程启动一个基础版,但是线程启动,肯定不是一个线程就可满足庞大的需求,因此,多线程是必不可少的,以下

import threading
base = 1

def inc():
    global base
    base += 1


def run_thread():
    for i in range(1000):
        inc()


thread1 = threading.Thread(target=run_thread, name='t1')
thread2 = threading.Thread(target=run_thread, name='t2')
thread1.start()
thread2.start()
thread1.join()
thread2.join()

print base

上面的代码就是一个简单的多线程实现版本,那么大家有没有想过代码的输出结果是怎样的?以下,我进行了n此实验,结果如下:

1713
1796
1583
1707
1636
2001
1723
1804
1921
...

对应结果,发现问题没,正常来说,结果应是2001,但是运行了这么多次,只出现一次2001,其他都比2001小,这是为什么呢?

回顾一下,线程之间是共享内存的,那么免不了内存竞争,那么上面代码到底在哪块发生了竞争,我们一步步进行分析。

  • base是一个全局变量,在inc()函数中发生了base += 1,如下
def inc():
    global base
    base += 1
  • base += 1进一步分解,如下
temp = base + 1
base = temp
  • 这样就发现了问题所在,temo为局部变量,每个线程都有自己的temp,代码正常执行时
temp1 = base+1  # temp1=2
base = temp1    # base=2
temp2 = base+1  # temp2=3
base = temp2    # base=3

# 结果
base = 3
  • 但是线程去共享资源,因此可能就会发生以下的操作
temp1 = base+1  # temp1=2
temp2 = base+1  # temp2=2
base = temp1    # base=2
base = temp2    # base=2 

# 结果
base = 2
  • 综上,要修改base时,需要多条语句联合完成,而执行这几条语句时,线程可能终端,导致多个线程将一个对象的内容改乱,因此就会出现上述的结果

加锁版

当然,我们肯定不希望数字这样变化,试想一下,去银行存钱时,存钱的过程中,钱缺被别人取走了,是什么样的体验,为此,必须保证修改base的时候,别的线程一定不能发生变动和修改。

因此,如果要确保base计算正确,就需要给inc()加上一把锁,当某个线程开始执行时inc()时,可以说该线程获得了锁,其他线程则不能同时执行inc(),只能等到锁被释放后,其他线程获取锁才可完成修改。而这个锁只有一把,因此同一时刻也只有一个线程持有该锁,便不会造成修改冲突。python的锁是通过threading.Lock()实现。

import threading

base = 1
lock = threading.Lock()

def inc():
    global base
    base += 1

def run_thread():
    for i in range(1000):
        lock.acquire()
        try:
            inc()
        finally:
            lock.release()


thread1 = threading.Thread(target=run_thread, name='t1')
thread2 = threading.Thread(target=run_thread, name='t2')
thread1.start()
thread2.start()
thread1.join()
thread2.join()

print base

# 结果
2001

以上,便是多线程加锁的实例,而获得锁的线程用完后一定要释放锁,否则那些苦苦等待锁的线程将永远等待下去,成为死线程。因此用try…finally来确保锁一定会被释放

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值