Python学习XII --- 多线程学习初步2 --- 锁的使用

总说

前面一篇讲了如何开启多线程, 并且单独执行. 然而这些线程是不会互相干扰的, 但是在很多情况下, 线程之间的执行会互相干扰,这就需要引入同步. 同步指的是有一些代码块在某个时刻只能被一个线程执行! 比如修改数据库, 更新文件等等. 我们需要引入同步原语来进行操作这些代码(称为临界区代码).
一般有两种: 锁和信号量. 锁没啥好说的, 信号量针对多线程竞争有限资源的情况.

先看一个例子, 这里例子随机初始化3-6个线程, 每个线程运行2-4秒.

from atexit import register
from random import randrange
from threading import Thread, current_thread, Lock
from time import sleep, ctime

class CleanOutputSet(set):
    def __str__(self):
        return ','.join(x for x in self)

# randomly create 3-6 threads, each of which sleeps for 2-4 seconds.
loops = (randrange(2,5) for x in range(randrange(3,7)))
remaining = CleanOutputSet()

lock = Lock()
def loop(nsec):
    myname = current_thread().name
    # lock.acquire()
    remaining.add(myname)
    print('[{0}] Started {1}'.format(ctime(), myname))
    # lock.release()
    sleep(nsec)
    # lock.acquire()
    remaining.remove(myname)
    print('{0} Completed {1} ({2} secs)'.format(ctime(), myname, nsec))
    print('  (remaining: {0})'.format(remaining or 'NONE'))
    # lock.release()

def main():
    for pause in loops:
        thread = Thread(target=loop, args=(pause,)).start()

@register
def _atexit():
    print('all done at:', ctime())

if __name__ == '__main__':
    main()

输出

[Tue Apr  3 15:53:01 2018] Started Thread-1
[Tue Apr  3 15:53:01 2018] Started Thread-2
[Tue Apr  3 15:53:01 2018] Started Thread-3
[Tue Apr  3 15:53:01 2018] Started Thread-4
Tue Apr  3 15:53:04 2018 Completed Thread-2 (3 secs)
  (remaining: Thread-1,Thread-3,Thread-4)
Tue Apr  3 15:53:04 2018 Completed Thread-4 (3 secs)
  (remaining: Thread-1,Thread-3)
Tue Apr  3 15:53:04 2018 Completed Thread-3 (3 secs)
Tue Apr  3 15:53:04 2018 Completed Thread-1 (3 secs)
  (remaining: NONE)
  (remaining: NONE)
all done at: Tue Apr  3 15:53:04 2018

可能会得到上面这种奇怪的输出, Thread-3Thread-1都是运行3秒, 以至于 ‘Thread-3’的print(' (remaining: {0})'.format(remaining or 'NONE')) 还没开始运行, Thread-1也结束了. 所以remaining返回的是NONE, 紧接着输出的是 Thread-1print(' (remaining: {0})'.format(remaining or 'NONE')). 因此会出现两个(remaining: NONE).

解决方案: 因为这里的输出都依赖于remaining这个变量. 因此, 多个线程如果都会修改某一个变量, 那么在这个变量改变的操作前后加上锁操作! 即前面acquire(), 后面release().

将上面代码的注释去掉,我们就会得到正确的输出:

[Tue Apr  3 16:04:55 2018] Started Thread-1
[Tue Apr  3 16:04:55 2018] Started Thread-2
[Tue Apr  3 16:04:55 2018] Started Thread-3
[Tue Apr  3 16:04:55 2018] Started Thread-4
[Tue Apr  3 16:04:55 2018] Started Thread-5
[Tue Apr  3 16:04:55 2018] Started Thread-6
Tue Apr  3 16:04:57 2018 Completed Thread-4 (2 secs)
  (remaining: Thread-1,Thread-2,Thread-5,Thread-3,Thread-6)
Tue Apr  3 16:04:57 2018 Completed Thread-1 (2 secs)
  (remaining: Thread-2,Thread-5,Thread-3,Thread-6)
Tue Apr  3 16:04:57 2018 Completed Thread-5 (2 secs)
  (remaining: Thread-2,Thread-3,Thread-6)
Tue Apr  3 16:04:57 2018 Completed Thread-2 (2 secs)
  (remaining: Thread-3,Thread-6)
Tue Apr  3 16:04:58 2018 Completed Thread-6 (3 secs)
  (remaining: Thread-3)
Tue Apr  3 16:04:58 2018 Completed Thread-3 (3 secs)
  (remaining: NONE)
all done at: Tue Apr  3 16:04:58 2018

使用上下文管理器

太麻烦了, 前面acquire(), 后面release(). 另一种写法就是with语句, 此时每个对象的上下文管理器负责在进入该套件之前调用acquire(), 结束之后调用 release()

def loop(nsec):
    myname = current_thread().name
    with lock:
        remaining.add(myname)

    sleep(nsec)

    with lock:
    remaining.remove(myname)
        print('{0} Completed {1} ({2} secs)'.format(ctime(), myname, nsec))
        print('  (remaining: {0})'.format(remaining or 'NONE'))
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值