关闭 AsynchronousFileChannel 的常用成语是什么?

What is a common idiom for closing AsynchronousFileChannel?

我刚刚发现下面的代码没有按预期工作。

try (AsynchronousFileChannel channel = open(path)) {
    channel.read(, , ,
                 new CompletionHandler() {
                         @Override
                         public void completed(Integer result, Long attachment) {
                         }
                         @Override
                         public void failed(Throwable exc, Long attachment) {
                             // channel already has been closed!!!
                         }
                 });
}

因为 AsynchronousFileChannel#read 方法 returns 立即并且 CompletionHandler 无法工作。

完成任务后关闭频道的常用成语是什么?

我应该这样做吗?

AsynchronousFileChannel channel = open(path);

channel.read(, , ,
             new CompletionHandler() {
                     @Override
                     public void completed(Integer result, Long attachment) {
                     }
                     @Override
                     public void failed(Throwable exc, Long attachment) {
                     }
             });

channel.close();

希望有人知道确保所有 in-progress AsynchronousFileChannel 回调完成的正确方法 - 我找不到合适的 API 调用来结束带有回调的读取操作。在测试中,我注意到 CompletionHandler 回调可能会在我的方法调用返回数据读取给它的调用者之后被调用,并且在离开 try with resources 块之后关闭 AsynchronousFileChannel:

try (AsynchronousFileChannel fc = AsynchronousFileChannel.open(p, StandardOpenOption.READ)) {
    var handler = new CompletionHandler<Integer,Long>() {
        public void completed(Integer result, Long position) {
            // ...
        }
        public void failed(Throwable exc, Long position) {
            // ...
        }
    };
    // Run some reads:
    fc.read(aByteBuffer, startPos, Long.valueOf(startPos), handler);
}
return blah; 
// "handler" still can receive callbacks after this point!

相反,我使用了一种在方法内部使用 CountDownLatch 的解决方法,这样 try-catch 块不会退出,直到所有 fc.read 调用都与 [=] 的相应回调相匹配17=].

try (AsynchronousFileChannel fc = AsynchronousFileChannel.open(p, StandardOpenOption.READ)) {

    // Work out how many fc.read calls you plan to do
    int numberOfReads = 1;
    CountDownLatch countdown = new CountDownLatch(numberOfReads);

    var handler = new CompletionHandler<Integer,Long>() {
        public void completed(Integer result, Long position) {
            // ...
            countdown.countDown();
        }
        public void failed(Throwable exc, Long position) {
            // ...
            countdown.countDown();
        }
    };

    // Kick off numberOfReads x fc.read
    fc.read(yourByteBuffer, startPos, Long.valueOf(startPos), handler);

    // Without next line the AsynchronousFileChannel may continue to run after return
    countdown.await();
}
return blah; 
// No more "handler" callbacks after this point

这不是一个理想的解决方案,因为它依赖于在创建 CountDownLatch 之前计算读取次数。相反,您可以考虑使用动态计数器或使用 non-callback 读取方法:Future<Integer> fut = read(ByteBuffer, long) 样式并在调用者线程中调用 fut.get();