首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >同步不同的任务(I/O侦听守护进程、提示调度程序、输出等)

同步不同的任务(I/O侦听守护进程、提示调度程序、输出等)
EN

Code Review用户
提问于 2016-09-29 13:19:23
回答 1查看 72关注 0票数 1

很快,我需要创建一个具有客户机/服务器架构的Java应用程序(客户端表示某种数学函数,当它们完成计算时,服务器将使用这些值并产生最终结果)。另外,应该有两种类型的取消,第一种允许通过输入符号来停止服务器,第二种允许通过三个选项显示周期性提示符(继续,不带提示,取消)。当用户看到提示符时,计算结果不应该干预控制台。您可以在这里看到完整的描述,名为实验室1

使用客户端生成的值(在这里我使用CompletionService)

代码语言:javascript
运行
复制
private void runFuturesConsumer() {
    new Thread(() -> {
        try {
            while (true) {
                Future<Integer> consumedFuture = completionService.take();
                Integer consumedValue = consumedFuture.get();

                if (Objects.equals(consumedValue, INTERRUPTION_VALUE)) {
                    break;  // clients were interrupted, stop futures consuming
                }           // happens after cancelServerFuture procedure call

                valuesContainer.add(consumedValue);

                if (consumedValue == SHORT_CIRCUIT_CONDITION) {
                    cancelServerFuture();
                    transferResult(true, null);
                    break;
                }

                if (valuesContainer.size() == clientsNumber) {
                    transferResult(false, null);
                    break;
                }
            }
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }).start();
}

将结果从服务器传输到ApplicationManager (处理输出/输入和取消任务的类)

代码语言:javascript
运行
复制
private void transferResult(boolean isShortCircuited, String failureReport) {

    // simple lock/sync will produce a deadlock (if computations were completed during the prompt)
    if (outputLock.tryLock()) {
        try {
            if (failureReport != null) {
                serverListener.onFailureReported(failureReport);
            } else if (isShortCircuited) {
                serverListener.onCompletedComputation(SHORT_CIRCUIT_CONDITION, true);
            } else {
                serverListener.onCompletedComputation(valuesContainer.stream()
                        .reduce(1, (accumulator, elem) -> accumulator * elem), false);
            }
        } finally {
            cancelServerFuture();
            outputLock.unlock();
        }
    }
}

将在用户选择停止执行时调用。

代码语言:javascript
运行
复制
void stopServer() {
    if (outputLock.tryLock()) {
        transferResult(false, "stopped before the completion");
    }
}

此守护进程等待某人输入"q“,然后停止服务器。

代码语言:javascript
运行
复制
private void runKeyPressDaemon() {
    Thread thread = new Thread(() -> {
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));

        String message = null;
        while (true) {
            try {
                message = bufferedReader.readLine();
            } catch (IOException e) {
                e.printStackTrace();
            }

            if (message != null && message.equals("q")) {
                // it's definitely bad if scheduler decides to reschedule here
                // because output lock happens only inside stop server method
                // (trying to lock right after readLine procedure creates possibility of losing the result)
                server.stopServer();
                break;
            }
        }
    });

    thread.setDaemon(true);
    thread.start();
}

处理第二类取消(提示)

代码语言:javascript
运行
复制
private void runPromptScheduler() {
    scheduledExecutor.scheduleWithFixedDelay(this::displayPrompt, INITIAL_DELAY, SCHEDULER_PERIOD, TimeUnit.SECONDS);
}

private void displayPrompt() {

    promptLock.lock();
    try {
        Scanner scanner = new Scanner(System.in);

        System.out.print("continue(1), continue without prompt(2), cancel(3): ");
        int readValue = scanner.nextInt();

        switch (readValue) {
            case 1:
                handlePrompt(PromptOptions.CONTINUE);
                break;
            case 2:
                handlePrompt(PromptOptions.CONTINUE_WITHOUT_PROMPT);
                break;
            case 3:
                handlePrompt(PromptOptions.CANCEL);
                break;
        }
    } finally {
        promptLock.unlock();
    }
}

private void handlePrompt(PromptOptions promptOptions) {
    switch (promptOptions) {
        case CONTINUE:
            break;
        case CONTINUE_WITHOUT_PROMPT:
            scheduledExecutor.shutdownNow();
            break;
        case CANCEL:
            server.stopServer();
            break;
    }
}

响应结果输出

代码语言:javascript
运行
复制
@Override
public void onCompletedComputation(int result, boolean isShortCircuited) {

    promptLock.lock();
    try {
        if (isShortCircuited) {
            System.out.println("result (short-circuit): " + result);
        } else {
            System.out.println("result: " + result);
        }
    } finally {
        scheduledExecutor.shutdownNow();
        promptLock.unlock();
    }
}

@Override
public void onFailureReported(String cause) {

    promptLock.lock();
    try {
        System.out.println("failure caused by: " + cause);
    } finally {
        scheduledExecutor.shutdownNow();
        promptLock.unlock();
    }
}

在我的解决方案中,我不喜欢的是存在某种种族条件,因为不清楚谁将获得“想要的”outputLock。它可以是取消任务(输入符号或提示符)或FuturesConsumer (如果它消耗了所有的期货(例如,查看来自keyPressDaemon方法的注释,用户可以输入"q",但是FuturesConsumer可以占用CPU时间并打印结果)。我可以将这个outputLock移动到FuturesConsumer方法,但它不会解决问题。另外,我认为通过使用锁而不是最好的解决方案来禁止结果输出(当有活动的提示符时)。提前感谢您的建议和帮助。

EN

回答 1

Code Review用户

回答已采纳

发布于 2016-10-02 11:50:22

一些我注意到的小问题(我应该停止用这样的句子来开始我的答案):

  • 你在光着身子干活。这通常被认为是个坏主意,因为他们不那么容易清理。我们倾向于使用ExecutorService和相关的类。有趣的是你已经这么做了.
  • 当调用runFuturesConsumerrunKeyPressDaemon时,您总是启动线程。这很糟糕,因为它在任何地方都没有文档记录,我希望该方法能够跟踪您试图运行的内容是否已经在运行,并且在不需要时不会启动另一个实例。
  • 您没有发布cancelServerFuture的代码,但是命名表明了一些令人不安的东西。取消通常意味着没有结果,但您的代码暗示应该有?也许吧?,我不认为取消服务器计算,然后将一些东西传输给服务器(短路就是这样做的)。或者我只是遗漏了什么..。
  • 你把一个用户取消作为一个失败处理。我觉得这是误导。用户取消从不是失败。然而,在用户取消计算之后,您将显示消息"failure caused by: stopped before the completion"。这并不是预期的行为。相反,我希望要么没有消息,要么什么会产生"Successfully cancelled execution"的效果。
  • 没有显示您使用的锁是什么的代码,但是从代码中如何使用它们来判断,我不确定您最初是否真的需要它们,如果您需要它们,是否应该这样锁定它们…根据我对代码的理解,唯一真正必要的锁应该是outputLock (它也应该用于提示符)
  • 您使用的中断过程通常称为POISON_PILL。至少在我听到的时候,这是传统的名字。当我们处于中断状态时:通过Object#equals比较INTERRUPTION_VALUE和通过引用等于(==)比较其他情况是很有趣的。我不太清楚这是为什么,但唉.
  • 你对提示符的处理太复杂了。而不是调用handlePrompt (并且在那里有效地嵌套一个开关块),您可以在那里“内联”执行。附加注意:“不正确”的用户输入没有在那里处理。传入"asd“只会用InputMismatchException来炸掉代码:(

好吧,这应该涵盖大部分内容,我强烈建议进行后续审查,尽管如此:)

票数 3
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/142810

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档