在这种情况下,多线程如何帮助提高性能?

Posted

技术标签:

【中文标题】在这种情况下,多线程如何帮助提高性能?【英文标题】:How can multithreading help increase performance in this situation? 【发布时间】:2017-04-20 13:06:14 【问题描述】:

我有一段这样的代码:

while()

    x = jdbc_readOperation();

    y = getTokens(x);

    jdbc_insertOperation(y);


public List<String> getTokens(String divText)
    List<String> tokenList = new ArrayList<String>();
    Matcher subMatcher = Pattern.compile("\\[[^\\]]*]").matcher(divText);
    while (subMatcher.find()) 
        String token = subMatcher.group();
        tokenList.add(token);
    
    return tokenList;

据我所知,当一个线程被 I/O 或网络阻塞时,使用多线程可以节省时间。在此同步操作中,每个步骤都必须等待其上一步完成。我想要的是最大化 getTokens() 上的 cpu 利用率。

我的第一个想法是将getTokens()放在一个类的run方法中,并创建多个线程。但我认为它不会起作用,因为它似乎无法通过在纯计算操作上使用多个线程来获得性能优势。

在这种情况下,采用多线程是否有助于提高性能?如果是这样,我该怎么做?

【问题讨论】:

请问some_stuff()正在做的“事情”是否可以并行完成?或者,只有一个线程这样做才有意义? 没有通用的方法可以做到这一点。这完全取决于some_stuff() 所做的事情以及这段代码在整个程序中的使用方式。 抱歉有歧义,some_stuff() 是一个字符串处理函数,唯一的参数来自读取操作的结果。我已经在上面更新了。但我不确定在这种情况下是否可以“并行”完成,您能否详细说明一下?谢谢。 如果some_stuff()完全依赖x,而y完全依赖some_stuff(),那么并行化这三个步骤是无法提高性能的。可能some_stuff() 本身可以在内部并行化,但只有你自己知道。 divText 的内容(或者 x,如果你愿意的话) - getTokens 方法的输入是什么样的?是用空格还是其他符号分隔的单词? 【参考方案1】:

这将取决于 jdbc_readOperation() 生成要处理的数据的速度与 getTokens(x) 处理数据的速度的比较。知道这将帮助您确定多线程是否会帮助您。

你可以尝试这样的事情(只是为了让你明白):

int workToBeDoneQueueSize = 1000;
int workDoneQueueSize = 1000;
BlockingQueue<String> workToBeDone = new LinkedBlockingQueue<>(workToBeDoneQueueSize);
BlockingQueue<String> workDone = new LinkedBlockingQueue<>(workDoneQueueSize);

new Thread(() -> 
    try 
        while (true) 
            workToBeDone.put(jdbc_readOperation());
        
     catch (InterruptedException e) 
        e.printStackTrace();
        // handle InterruptedException here
    
).start();

int numOfWorkerThreads = 5; // just an example
for (int i = 0; i < numOfWorkerThreads; i++) 
    new Thread(() -> 
        try 
            while (true) 
                workDone.put(getTokens(workToBeDone.take()));
            
         catch (InterruptedException e) 
            e.printStackTrace();
            // handle InterruptedException here
        
    ).start();


new Thread(() -> 
    // you could improve this by making a batch operation
    try 
        while (true) 
            jdbc_insertOperation(workDone.take());
        
     catch (InterruptedException e) 
        e.printStackTrace();
        // handle InterruptedException here
    
).start();

或者您可以学习如何使用 ThreadPoolExecutor。 (https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ThreadPoolExecutor.html)

【讨论】:

谢谢,这很有帮助。在这种情况下是否有任何理由使用 LinkedBlockingQueue 而不是 ArrayBlockingQueue? 不,我不这么认为,你也可以使用。【参考方案2】:

为了加快 getTokens() 的速度,您可以使用 String.substring() 方法拆分输入的 String divText。您将其拆分为尽可能多的子字符串,因为您将运行运行 getTokens() 方法的线程。然后每个线程都会在 divText 的某个子字符串上“运行”。

应避免创建超出 CPU 处理能力的线程,因为上下文切换会导致效率低下。

https://docs.oracle.com/javase/8/docs/api/java/lang/String.html#substring-int-int-

另一种方法是使用 String.split 方法 http://docs.oracle.com/javase/7/docs/api/java/lang/String.html#split%28java.lang.String%29 拆分输入的 getTokens 字符串,例如如果文本由空格或其他符号分隔的单词组成。然后可以将生成的字符串数组的特定部分传递给不同的线程。

【讨论】:

这行不通。如何预先确定应该从哪些点获取子字符串?每当您采用子字符串时,它可能会拆分可能与模式匹配的字符串,如果它们保持原样。 好点,拆分有点困难。您必须检查文本中单词的最大长度,并检查您是否没有在此处剪切(之前和之后)。如果 divText 包含由空格分隔的单词,则可以通过检查空格来定位最大单词长度指定的该区域来计算拆分索引。

以上是关于在这种情况下,多线程如何帮助提高性能?的主要内容,如果未能解决你的问题,请参考以下文章

C#多线程编程:线程池ThreadPool

Java多线程-新特征-锁(下)

将多线程合并到 C++ 中如何提高性能,为啥?

如何提高报表的取数性能

如何提高datapump操作性能

谷歌计算引擎提高单线程性能