我已经使用其他SO用户建议的更新代码更新了问题,并将澄清之前存在的任何含糊不清的文字。
我只能访问相关应用程序生成的日志文件。因此,我被限制在日志文件的内容中工作,并且没有超出该范围的解决方案。我稍微修改了样本数据。我想指出以下关键变量。
Thread ID
- 范围从0..19 - 线程被多次使用。因此ScriptExecThread(2)
可能会在日志中多次出现。
Script
- 每个线程都会在特定文件上运行脚本。同样的脚本可能在同一个线程上运行,但不会在同一个线程AND文件上运行。
File
- 每个Thread ID
在Script
上运行File
。如果Thread(10)
在myscript.script
上运行myfile.file
,则该EXACT行不会再次执行。使用上述示例的成功示例就是这样。
------ ------ START
线程(10)在myfile.file上启动myscript.script
线程(10)在myfile.file上完成了myscript.script
------ ------- END
使用上述示例的不成功示例将是:
------ ------ START
线程(10)在myfile.file上启动myscript.script
------ ------ END
在解决我的查询之前,我将简要介绍所使用的代码和所需的行为。
我目前正在解析大型日志文件(平均占用100k - 600k行)并尝试按特定顺序检索某些信息。我已经根据我的要求制定了布尔代数,它似乎在纸上起作用但在代码上却没有那么多(我必须错过了一些显而易见的东西)。我想提前告知代码没有任何形状或形式优化,现在我只是想让它工作。
在此日志文件中,您可以看到某些线程在启动时挂起但从未完成。可能的线程ID范围的数量。这是一些伪代码:
REGEX = "ScriptExecThread(\\([0-9]+\\)).*?(finished|starting)" //in java
Set started, finished
for (int i=log.size()-1; i >=0; i--) {
if(group(2).contains("starting")
started.add(log.get(i))
else if(group(2).contains("finished")
finished.add(log.get(i)
}
started.removeAll(finished);
Set<String> started = new HashSet<String>(), finished = new HashSet<String>();
for(int i = JAnalyzer.csvlog.size()-1; i >= 0; i--) {
if(JAnalyzer.csvlog.get(i).contains("ScriptExecThread"))
JUtility.hasThreadHung(JAnalyzer.csvlog.get(i), started, finished);
}
started.removeAll(finished);
commonTextArea.append("Number of threads hung: " + noThreadsHung + "\n");
for(String s : started) {
JLogger.appendLineToConsole(s);
commonTextArea.append(s+"\n");
}
public static boolean hasThreadHung(final String str, Set<String> started, Set<String> finished) {
Pattern r = Pattern.compile("ScriptExecThread(\\([0-9]+\\)).*?(finished|starting)");
Matcher m = r.matcher(str);
boolean hasHung = m.find();
if(m.group(2).contains("starting"))
started.add(str);
else if (m.group(2).contains("finished"))
finished.add(str);
System.out.println("Started size: " + started.size());
System.out.println("Finished size: " + finished.size());
return hasHung;
}
ScriptExecThread(1)在afile.xyz
上启动ScriptExecThread(2)在bfile.abc
上启动ScriptExecThread(3)在cfile.zyx
上启动ScriptExecThread(4)在dfile.zxy
上启动ScriptExecThread(5)在efile.yzx
上启动ScriptExecThread(1)在afile.xyz上完成
ScriptExecThread(2)在bfile.abc上完成
ScriptExecThread(3)在cfile.zyx上完成
ScriptExecThread(4)在dfile.zxy
上完成ScriptExecThread(5)在efile.yzy
上完成ScriptExecThread(1)在bfile.abc
上启动ScriptExecThread(2)在dfile.zxy
上启动ScriptExecThread(3)在afile.xyz
上启动ScriptExecThread(1)在bfile.abc上完成
结束日志
如果您举例,您会注意到2号线和2号线。 3开始但未能完成(原因没有必要,我只需要获得该线)。
09.08 15:06.53,ScriptExecThread(7),Info,########### starting
09.08 15:06.54,ScriptExecThread(18),Info,#######################
09.08 15:06.54,ScriptExecThread(13),Info,########在#########
中完成09.08 15:06.54,ScriptExecThread(13),Info,########## starting
09.08 15:06.55,ScriptExecThread(9),Info,#####在########
中完成09.08 15:06.55,ScriptExecThread(0),Info,####在###########
中完成09.08 15:06.55,ScriptExecThread(19),Info,####在########
中完成09.08 15:06.55,ScriptExecThread(8),Info,######在2777完成#########
09.08 15:06.55,ScriptExecThread(19),Info,########## starting
09.08 15:06.55,ScriptExecThread(8),Info,####### starting
09.08 15:06.55,ScriptExecThread(0),Info,########## starting
09.08 15:06.55,ScriptExecThread(19),Info,Post ######在#####
完成09.08 15:06.55,ScriptExecThread(0),Info,######在#########
中完成09.08 15:06.55,ScriptExecThread(19),Info,########## starting
09.08 15:06.55,ScriptExecThread(0),Info,########### starting
09.08 15:06.55,ScriptExecThread(9),Info,########## starting
09.08 15:06.56,ScriptExecThread(1),Info,#######在########
中完成09.08 15:06.56,ScriptExecThread(17),Info,######在#######
中完成09.08 15:06.56,ScriptExecThread(17),Info,#######################
09.08 15:06.56,ScriptExecThread(1),Info,########## starting
目前,代码只显示整个日志文件,其中的行以&#34;开始&#34;开头。当我查看代码时,这有点有意义。
我删除了任何我不希望显示的冗余信息。如果有什么我可能遗漏的请随时告诉我,我会添加它。
答案 0 :(得分:6)
如果我理解正确,你有大文件,并试图找到表格的模式&#34; X开始(但没有提到X完成)&#34;对于X的所有数值。
如果我这样做,我会使用这个伪代码:
Pattern p = Pattern.compile(
"ScriptExecThread\\(([0-9]+).*?(finished|started)");
Set<Integer> started, finished;
Search for p; for each match m,
int n = Integer.parseInt(m.group(1));
if (m.group(2).equals("started")) started.add(n);
else finished.add(n);
started.removeAll(finished); // found 'em: contains started-but-not-finished
这需要在每个文件上进行单个正则表达式传递,并且需要O(完成大小)设置减法;它应该比您当前的方法快20倍。正则表达式将使用可选(|
)匹配来同时查找两个备选项,从而减少传递次数。
编辑:显式正则表达式。编译正则表达式而不是每行一次应该可以减少一些额外的运行时间。
编辑2:实现伪代码,适合我
编辑3:替换实现以显示文件&amp;线。减少内存需求(不将整个文件加载到内存中);但打印线确实需要所有&#34; start&#34;要存储的行。
public class T {
public static Collection<String> findHung(Iterable<String> data) {
Pattern p = Pattern.compile(
"ScriptExecThread\\(([0-9]+).*?(finished|starting)");
HashMap<Integer, String> started = new HashMap<Integer, String>();
Set<Integer> finished = new HashSet<Integer>();
for (String d : data) {
Matcher m = p.matcher(d);
if (m.find()) {
int n = Integer.parseInt(m.group(1));
if (m.group(2).equals("starting")) started.put(n, d);
else finished.add(n);
}
}
for (int f : finished) {
started.remove(f);
}
return started.values();
}
static Iterable<String> readFile(String path, String encoding) throws IOException {
final Scanner sc = new Scanner(new File(path), encoding).useDelimiter("\n");
return new Iterable<String>() {
public Iterator<String> iterator() { return sc; }
};
}
public static void main(String[] args) throws Exception {
for (String fileName : args) {
for (String s : findHung(readFile(fileName, "UTF-8"))) {
System.out.println(fileName + ": '" + s + "' unfinished");
}
}
}
}
输入:上面的示例数据,作为第一个参数,称为&#34; data.txt&#34;。另一个文件中的相同数据称为&#34; data2.txt&#34;作为第二个参数(javac T.java && java T data.txt data2.txt
)。输出:
data.txt: ' 09.08 15:06.54, ScriptExecThread(18),Info,###################### starting' unfinished
data.txt: ' 09.08 15:06.53, ScriptExecThread(7),Info,########### starting' unfinished
data2.txt: ' 09.08 15:06.54, ScriptExecThread(18),Info,###################### starting' unfinished
data2.txt: ' 09.08 15:06.53, ScriptExecThread(7),Info,########### starting' unfinished
答案 1 :(得分:2)
保留两组独立的started
和finished
线程(如@tucuxi所述)无效。如果ID为5的线程启动,运行并完成,则finished
集中将永久显示5。如果ID为5的另一个线程启动并挂起,则不会报告。
但是,假设片刻ID不会被重用。创建的每个线程都会收到一个新ID。通过保留单独的started
和finished
集,您在完成时将拥有数十万个元素。这些数据结构的性能与它们在操作时的性能成正比。性能不太可能对您的用例很重要,但如果您执行的操作更昂贵,或者运行的数据要大100倍,那么它可能会。
序言,这是@ tucuxi代码的工作版本:
import java.util.*;
import java.io.*;
import java.util.regex.*;
public class T {
public static Collection<String> findHung(Iterable<String> data) {
Pattern p = Pattern.compile(
"ScriptExecThread\\(([0-9]+).*?(finished|starting)");
HashMap<Integer, String> running = new HashMap<Integer, String>();
for (String d : data) {
Matcher m = p.matcher(d);
if (m.find()) {
int n = Integer.parseInt(m.group(1));
if (m.group(2).equals("starting"))
running.put(n, d);
else
running.remove(n);
}
}
return running.values();
}
static Iterable<String> readFile(String path, String encoding) throws IOException {
final Scanner sc = new Scanner(new File(path), encoding).useDelimiter("\n");
return new Iterable<String>() {
public Iterator<String> iterator() { return sc; }
};
}
public static void main(String[] args) throws Exception {
for (String fileName : args) {
for (String s : findHung(readFile(fileName, "UTF-8"))) {
System.out.println(fileName + ": '" + s + "' unfinished");
}
}
}
}
请注意,我已删除了finished
集,HashMap
现在称为running
。当新线程开始时,它们会进入,当线程完成时,它会被拉出。这意味着HashMap
将始终是当前正在运行的线程数的大小,它始终小于(或等于)运行的线程总数。因此对它的操作会更快。 (作为一个令人愉快的副作用,您现在可以按日志行跟踪日志行上运行的线程数,这可能有助于确定线程挂起的原因。)
这是我用来生成大量测试用例的Python程序:
#!/usr/bin/python
from random import random, choice
from datetime import datetime
import tempfile
all_threads = set([])
running = []
hung = []
filenames = { }
target_thread_count = 16
hang_chance = 0.001
def log(id, msg):
now = datetime.now().strftime("%m:%d %H:%M:%S")
print "%s, ScriptExecThread(%i),Info,%s" % (now, id, msg)
def new_thread():
if len(all_threads)>0:
for t in range(0, 2+max(all_threads)):
if t not in all_threads:
all_threads.add(t)
return t
else:
all_threads.add(0)
return 0
for i in range(0, 100000):
if len(running) > target_thread_count:
new_thread_chance = 0.25
else:
new_thread_chance = 0.75
pass
if random() < new_thread_chance:
t = new_thread()
name = next(tempfile._get_candidate_names())+".txt"
filenames[t] = name
log(t, "%s starting" % (name,))
if random() < hang_chance:
hung.append(t)
else:
running.append(t)
elif len(running)>0:
victim = choice(running)
all_threads.remove(victim)
running.remove(victim)
log(t, "%s finished" % (filenames[victim],))
答案 2 :(得分:1)
value
永远不会奏效
removeAll
存储整个字符串
因此hasThreadHung
中的值永远不会与started
中的值匹配。
你想做类似的事情:
finished
然后在class ARecord {
// Proper encapsulation of the members omitted for brevity
String thread;
String line;
public ARecord (String thread, String line) {
this.thread = thread;
this.line = line;
}
public int hashcode() {
return thread.hashcode();
}
public boolean equals(ARecord o) {
return thread.equals(o.thread);
}
}
中,创建hasHungThread
并将其添加到ARecord
。
例如:
Set
在started.add(new ARecord(m.group(2), str));
中,您将从searchHungThreads
检索ARecord
并将其输出为:
started
答案 3 :(得分:0)
为什么不以另一种方式解决问题。如果你想要的只是挂起线程,可以通过编程方式获取线程堆栈。也可以使用外部工具但内部拥有JVM我认为最简单。然后将其作为API公开或使用线程转储定期保存带日期时间戳的文件。另一个程序只需要分析线程转储。如果相同的线程在相同的位置(相同的堆栈跟踪或相同的3-5个函数)通过线程转储它很有可能它挂起。
有些工具可以帮助您分析https://www.google.com/search?q=java+thread+dump+tool+open+source