【发布时间】:2011-03-14 11:37:59
【问题描述】:
我遍历大量下载的文本文件中的行,并在每一行上进行正则表达式匹配。通常,比赛需要不到一秒钟的时间。然而,有时一场比赛需要几分钟,有时比赛根本没有完成,代码只是挂起(几次等了一个小时,然后放弃了)。因此,我需要引入某种超时,并以某种方式告诉正则表达式匹配代码在 10 秒左右后停止。我可以接受这样一个事实,即我将丢失正则表达式应该返回的数据。
我尝试了以下方法(当然,这已经是一个代码示例中显示的 2 种不同的基于线程的解决方案):
def timeout_handler():
print 'timeout_handler called'
if __name__ == '__main__':
timer_thread = Timer(8.0, timeout_handler)
parse_thread = Thread(target=parse_data_files, args=(my_args))
timer_thread.start()
parse_thread.start()
parse_thread.join(12.0)
print 'do we ever get here ?'
但我在输出中既没有得到timeout_handler called 也没有得到do we ever get here ? 行,代码只是卡在parse_data_files 中。
更糟糕的是,我什至无法用CTRL-C 停止程序,而是需要查找python 进程号并终止该进程。一些研究表明,Python 人知道正则表达式 C 代码会跑掉:http://bugs.python.org/issue846388
我确实使用信号实现了一些成功:
signal(SIGALRM, timeout_handler)
alarm(8)
data_sets = parse_data_files(config(), data_provider)
alarm(0)
这让我得到了输出中的timeout_handler called 行——我仍然可以使用CTRL-C 停止我的脚本。如果我现在像这样修改 timeout_handler:
class TimeoutException(Exception):
pass
def timeout_handler(signum, frame):
raise TimeoutException()
并将对re.match(...) 的实际调用包含在try ... except TimeoutException 子句中,正则表达式匹配实际上会被中断。不幸的是,这只适用于我用来尝试东西的简单的单线程沙盒脚本。这个解决方案有一些问题:
- 信号只触发一次,如果有不止一条有问题的线路,我会卡在第二条上
- 计时器从那里开始计数,而不是在实际解析开始时
- 由于 GIL,我必须在主线程中进行所有信号设置,并且信号仅在主线程中接收;这与多个文件要在单独的线程中同时解析的事实相冲突 - 也只引发了一个全局超时异常,我不知道如何知道我需要在哪个线程中对其做出反应
- 我已经读了好几遍了,线程和信号不能很好地混合
我也考虑过在一个单独的过程中进行正则表达式匹配,但在我开始之前,我想我最好在这里检查一下是否有人以前遇到过这个问题,并且可以给我一些关于如何解决它的提示.
更新
正则表达式看起来像这样(好吧,无论如何,其中一个,其他正则表达式也会出现问题;这是最简单的一个):
'^(\d{5}), .+?, (\d{8}), (\d{4}), .+?, .+?,' + 37 * ' (.*?),' + ' (.*?)$'
样本数据:
95756, "KURN ", 20110311, 2130, -34.00, 151.21, 260, 06.0, -9999.0, -9999.0, -9999.0, -9999.0, -9999.0, -9999, -9999, 07.0, -9999, -9999, -9999, -9999, -9999, -9999, -9999, -9999, -9999, -9999, -9999, -9999, -9999, -9999, -9999, -9999, -9999, -9999, -9999, -9999, -9999, -9999, -9999, -9999, -
如前所述,正则表达式 通常 执行良好 - 我可以在不到一分钟的时间内解析数百行的数百个文件。但是,那是文件完整的时候-代码似乎与行不完整的文件一起挂起,例如
`95142, "YMGD ", 20110311, 1700, -12.06, 134.23, 310, 05.0, 25.8, 23.7, 1004.7, 20.6, 0.0, -9999, -9999, 07.0, -9999, -9999, -9999, -9999, -9999, -9999, -9999, -9999, -9999, -9999, -9999, -9999, -9999, -9999, -9999, -9999, -9999, -9999, -9999, -9999
我也确实遇到了正则表达式似乎立即返回并报告不匹配的情况。
更新 2
我只是快速阅读了catastrophic article,但据我所知,这不是原因 - 我没有嵌套任何重复运算符。
我在 Mac OSX 上,所以我不能使用 RegexBuddy 来分析我的正则表达式。我尝试了RegExhibit(它显然在内部使用了 Perl RegEx 引擎)——它也跑掉了。
【问题讨论】:
-
Python 中的正则表达式匹配发生在内部 GIL,因此线程解决方案对您没有帮助:当正则表达式匹配运行时,没有其他线程可以运行。
-
这种性能问题通常是由于灾难性的回溯造成的。您很可能可以改进您的正则表达式以更快地匹配(或失败)。请发布您正在使用的正则表达式(以及您与之匹配的一些示例数据),以便我们查看它。解决问题,而不是症状:)
标签: python regex multithreading timeout signals