具有Replay和FirstAsync + ForEachAsync的IObservable奇怪的副作用

时间:2015-10-22 04:54:27

标签: c# c#-4.0 system.reactive reactive-programming

我的代码问题如下。我有一个冷源,从你订阅时开始。我想运行一次Observable,所以我正在使用重播调用。我发现当它命中条件分支来编写头时,它会在调用FirstAsync时启动Observable,然后在ForEachAsync调用的新线程中再次启动Observable。我最终在两个线程中以可观察的方式运行。我不确定为什么会这样。

public async Task WriteToFileAsync(string filename, IObservable<IFormattableTestResults> source, bool overwrite)
{
    ThrowIfInvalidFileName(filename);
    var path = Path.Combine(_path, filename);
    bool fileExists = File.Exists(path);

    using (var writer = new StreamWriter(path, !overwrite))
    {
        var replay = source.Replay().RefCount();

        if (overwrite || !fileExists)
        {
            var first = await replay.FirstAsync();
            var header = GetCsvHeader(first.GetResults());
            await writer.WriteLineAsync(header);
        }

        await replay.ForEachAsync(result => writer.WriteLineAsync(FormatCsv(result.GetResults())));
    }
}

编辑2015年10月22日:添加更多代码

private Task RunIVBoardCurrentAdjust(IVBoardAdjustment test)
{
    logger.Info("Loading IV board current adjustment test.");
    var testCases = _testCaseLoader.GetIVBoardCurrentAdjustTests().ToArray();
    var source = test.RunCurrentAdjustment(testCases);
    return _fileResultsService.WriteToFileAsync("IVBoardCurrentAdjust.csv", source, false);
}

public IObservable<IVBoardCurrentAdjustTestResults> RunCurrentAdjustment(IEnumerable<IVBoardCurrentAdjustTestCase> testCases)
{
    return
        testCases
        .Select(RunCurrentAdjustment)
        .Concat();
}

public IObservable<IVBoardCurrentAdjustTestResults> RunCurrentAdjustment(IVBoardCurrentAdjustTestCase testCase)
{
    logger.Debug("Preparing IV board current adjustment procedure.");
    return Observable.Create<IVBoardCurrentAdjustTestResults>(
        (observer, cancelToken) =>
    {
        var results =
            RunAdjustment(testCase)
                .Do(result => logger.Trace(""))
                .Select(
                    (output, i) =>
                        new IVBoardCurrentAdjustTestResults(i, testCase, output)
                        {
                            Component = "IV Board",
                            Description = "Characterization (Secant Method)"
                        })
                .Replay();
        results.Subscribe(observer, cancelToken);
        var task = StoreResultInBTD(results, testCase, 1/testCase.Load);
        results.Connect();
        return task;
    });
}

private IObservable<IRootFindingResult> RunAdjustment<T>(IVBoardAdjustTestCase<T> testCase) where T : DacCharacterizationSecantInput
{
    logger.Debug("Initializing IV board test.");
    SetupTest(testCase);
    return
        new DacCharacterization()
            .RunSecantMethod(
                code => _yellowCake.IVBoard.DacRegister.Value = code,
                () => _dmm.Read(),
                GetTestInputs(testCase));
}

private async Task StoreResultInBTD(IObservable<IVBoardAdjustTestResults> results, IVBoardAdjustTestCase testCase, double targetScalingFactor = 1)
{
    var points =
            results
                .Select(
                    result =>
                        new IVBoardCharacteristicCurveTestPoint(
                            (result.Output.Target - result.Output.Error) * targetScalingFactor,
                            (int)result.Output.Root));

    var curve = await points.ToArray();
    _yellowCake.BoardTest.WriteIVBoardAjust(curve, testCase.Mode, testCase.Range);
    _yellowCake.BoardTest.SaveToFile();
}

private IEnumerable<DacCharacterizationSecantInput> GetTestInputs<T>(IVBoardAdjustTestCase<T> testCase) where T : DacCharacterizationSecantInput
{
    foreach (var input in testCase.Inputs)
    {
        logger.Debug("Getting next test input.");
        _dmm.Config.PowerLineCycles.Value = input.IntegrationTime;
        yield return input.Input;
    }
}

public IObservable<IRootFindingResult> RunSecantMethod(
    Action<int> setDacOutput,
    Func<double> readMeanOutput,
    IEnumerable<DacCharacterizationSecantInput> inputs)
{
    var search = new SecantSearch();
    var param = SecantMethodParameter.Create(setDacOutput, readMeanOutput);

    return
        Observable
            .Create<IRootFindingResult>(
                (observer, cancelToken) =>
                    Task.Run(() =>
                    {
                        foreach (var input in inputs)
                        {
                            cancelToken.ThrowIfCancellationRequested();
                            var result =
                                search.FindRoot(
                                    param.SearchFunction,
                                    input.FirstGuess,
                                    input.SecondGuess,
                                    input.Target,
                                    input.SearchOptions,
                                    cancelToken,
                                    () => param.AdaptedDacCode);

                            if (!result.Converged)
                            {
                                observer.OnError(new FailedToConvergeException(result));
                            }

                            observer.OnNext(result);
                        }
                    }, cancelToken));
}

0 个答案:

没有答案