Python多线程中阻塞(join)与锁(Lock)使用误区解析

作者:Xyntax 时间:2022-03-22 08:00:31 

关于阻塞主线程

join的错误用法

Thread.join() 作用为阻塞主线程,即在子线程未返回的时候,主线程等待其返回然后再继续执行.

join不能与start在循环里连用
以下为错误代码,代码创建了5个线程,然后用一个循环激活线程,激活之后令其阻塞主线程.


threads = [Thread() for i in range(5)]
for thread in threads:
thread.start()
thread.join()

执行过程:

1. 第一次循环中,主线程通过start函数激活线程1,线程1进行计算.
2. 由于start函数不阻塞主线程,在线程1进行运算的同时,主线程向下执行join函数.
3. 执行join之后,主线程被线程1阻塞,在线程1返回结果之前,主线程无法执行下一轮循环.
4. 线程1计算完成之后,解除对主线程的阻塞.
5. 主线程进入下一轮循环,激活线程2并被其阻塞…

如此往复,可以看出,本来应该并发的五个线程,在这里变成了顺序队列,效率和单线程无异.

join的正确用法

使用两个循环分别处理start和join函数.即可实现并发.


threads = [Thread() for i in range(5)]
for thread in threads:
thread.start()
for thread in threads:
thread.join()

time.sleep代替join进行调试

之前在一些项目里看到过这样的代码,使用time.sleep代替join手动阻塞主线程.
在所有子线程返回之前,主线程陷入无线循环而不能退出.


for thread in threads:
thread.start()
while 1:
if thread_num == 0:
break
time.sleep(0.01)

关于线程锁(threading.Lock)

单核CPU+PIL是否还需要锁?

非原子操作 count = count + 1 理论上是线程不安全的.
使用3个线程同时执行上述操作改变全局变量count的值,并查看程序执行结果.
如果结果正确,则表示未出现线程冲突.

使用以下代码测试


# -*- coding: utf-8 -*-

import threading
import time
count = 0

class Counter(threading.Thread):
def __init__(self, name):
self.thread_name = name
super(Counter, self).__init__(name=name)

def run(self):
global count
for i in xrange(100000):
 count = count + 1

counters = [Counter('thread:%s' % i) for i in range(5)]
for counter in counters:
counter.start()

time.sleep(5)
print 'count=%s' % count

运行结果:

count=275552

事实上每次运行结果都不相同且不正确,这证明单核CPU+PIL仍无法保证线程安全,需要加锁.

加锁后的正确代码:


# -*- coding: utf-8 -*-

import threading
import time

count = 0
lock = threading.Lock()

class Counter(threading.Thread):
def __init__(self, name):
self.thread_name = name
self.lock = threading.Lock()
super(Counter, self).__init__(name=name)

def run(self):
global count
global lock
for i in xrange(100000):
 lock.acquire()
 count = count + 1
 lock.release()

counters = [Counter('thread:%s' % i) for i in range(5)]

for counter in counters:
counter.start()

time.sleep(5)
print 'count=%s' % count

结果:

count=500000

注意锁的全局性

这是一个简单的Python语法问题,但在逻辑复杂时有可能被忽略.
要保证锁对于多个子线程来说是共用的,即不要在Thread的子类内部创建锁.

以下为错误代码


# -*- coding: utf-8 -*-

import threading
import time

count = 0
# lock = threading.Lock() # 正确的声明位置

class Counter(threading.Thread):
def __init__(self, name):
self.thread_name = name
self.lock = threading.Lock() # 错误的声明位置
super(Counter, self).__init__(name=name)

def run(self):
global count
for i in xrange(100000):
 self.lock.acquire()
 count = count + 1
 self.lock.release()

counters = [Counter('thread:%s' % i) for i in range(5)]

for counter in counters:
print counter.thread_name
counter.start()

time.sleep(5)
print 'count=%s' % count

来源:https://blog.csdn.net/cd_xuyue/article/details/52052893

标签:python,多线程,阻塞,锁
0
投稿

猜你喜欢

  • asp如何制作一个安全的页面?

    2010-06-29 21:22:00
  • ADSI+ASP添加IP到IIS禁止访问列表中

    2011-04-02 10:42:00
  • 扫盲大讲堂:mysql出错的代码解析及解答

    2009-09-05 10:08:00
  • 编写数据库asp程序需注意的问题

    2007-12-29 12:57:00
  • 正则表达式语法速查

    2007-11-11 10:43:00
  • Oracle收购TimesTen 提高数据库软件性能

    2010-07-21 13:03:00
  • 深入研究Application和Session对象

    2008-10-13 08:30:00
  • mysql 主从服务器的简单配置

    2009-09-06 12:06:00
  • php的优点总结 php有哪些优点

    2023-11-14 16:03:25
  • PHP PDOStatement::fetchAll讲解

    2023-06-14 15:54:26
  • 获得当前数据库对象依赖关系的实用算法

    2009-01-08 13:28:00
  • python线程信号量semaphore使用解析

    2023-02-08 08:47:24
  • 详解Go语言变量作用域

    2023-08-05 03:25:43
  • 浅析Python 序列化与反序列化

    2023-05-01 14:36:16
  • ASP应用之教你使用模板

    2008-10-15 13:09:00
  • coreseek 搜索英文的问题详解

    2023-11-20 14:12:10
  • PDO::_construct讲解

    2023-06-06 03:22:31
  • asp如何利用当前时间生成随机函数?

    2010-01-01 15:44:00
  • Go语言使用Gob传输数据

    2023-08-06 05:12:28
  • java 截取字符串(判断汉字)

    2023-06-29 23:38:19
  • asp之家 网络编程 m.aspxhome.com