释放与项目关联的资源

时间:2019-07-08 14:36:39

标签: kotlin rx-java reactivex

我正在用RxJava3实现一些多阶段管道样式的处理。我对各个步骤使用了几个连续的.concatMap调用。副作用是,这些步骤会创建一些(大)临时文件,一旦出现错误和成功,应将其删除。第一步将文件移交给下一个文件。我成功使用Single.using关闭了文件句柄,但是无法以这种方式删除文件,因为在下一步使用它之前它已经消失了。在大多数情况下,可以在第一步的doOnError和第二步的using中删除文件。

但是,在极端情况下,文件“泄漏”即未被删除:如果第一个工作项的第二步失败(引发异常),第二个项完成后 它的第一步(第一个concatMap的第一步,但尚未开始其第二个步骤(第二个concatMap),该第二项位于某个中间位置,未被删除,因为它不是当前在任何using范围内捕获。

我的最小示例是:

import io.reactivex.Observable
import io.reactivex.schedulers.Schedulers
import java.io.File
import io.reactivex.Single
import java.io.FileOutputStream

fun main(args: Array<String>) {
    Observable.just(5, 4).subscribeOn(Schedulers.computation())
        .concatMapSingle { workItem ->
            val file = File("/tmp/foo/work$workItem")
            Single.using({ FileOutputStream(file) }, { oStream ->
                Single.just(oStream)
                    .subscribeOn(Schedulers.computation())
                    .map { os ->
                        println("Pretending to work on item $workItem")
                        os.write("File $workItem".toByteArray())

//                      if (Math.random () > 0.5) throw Exception ("Something else failed")

                        Thread.sleep(workItem.toLong() * 1000)  // Second work item should have a quicker first step than the second step of the first item
                        Pair(file, workItem)    // Propagate both file and item number
                    }.doOnError { println("Deleting ${file.absolutePath}"); file.delete() }
            }, { os -> println("Closing file ${file.absolutePath}"); os.close(); })
        }
        .concatMapSingle { data1 ->
            Single.using({ data1 }, { data2 ->
                Single.just(data2)
                    .subscribeOn(Schedulers.computation())
                    .map { data ->
                        val workItem = data.second
                        println("Continuing pretend work on item ${workItem}");

                        Thread.sleep(workItem.toLong() * 1000)

                        // ... More complex stuff here ...

                        if (workItem == 5) throw Exception("Something failed")
                    }
            }, { data -> println("Deleting ${data.first.absolutePath}"); data.first.delete(); })
        }.blockingSubscribe();
}

如果引发异常,则不会删除文件/tmp/foo/work4,因为工作项“ 4”等待第二个concatMap处理1。输出为:

Pretending to work on item 5
Closing file /tmp/foo/work5
Continuing pretend work on item 5
Pretending to work on item 4
Closing file /tmp/foo/work4
Deleting /tmp/foo/work5
Exception in thread "main" java.lang.RuntimeException: java.lang.Exception: Something failed
    at io.reactivex.internal.util.ExceptionHelper.wrapOrThrow(ExceptionHelper.java:46)
    [...]

如果第一个(注释掉)或没有异常抛出,则一切都将被删除。问题与flatMap相同,但是由于并行运行的事物越来越多,调试起来更加困难。

因此,我的问题是:我可以将某些“清理”功能与该项目超出范围时总是调用的项目(此处为:5、4)相关联吗?

0 个答案:

没有答案