C# IObservable与Replay和FirstAsync+;ForEachAsync奇怪的副作用

C# IObservable与Replay和FirstAsync+;ForEachAsync奇怪的副作用,c#,c#-4.0,system.reactive,reactive-programming,C#,C# 4.0,System.reactive,Reactive Programming,我下面的代码有问题。我有一个冷源,当你订阅时开始。我想运行Observable一次,所以我对它使用了重播调用。我发现,当它点击条件分支写入报头时,它会在调用FirstAsync时启动Observable,然后在ForEachAsync调用时在新线程中再次启动Observable。我最终发现,可观察到的在两个线程中同时运行。我不知道为什么会发生这种情况 public async Task WriteToFileAsync(string filename, IObservable<IForma

我下面的代码有问题。我有一个冷源,当你订阅时开始。我想运行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())));
    }
}
public async Task WriteToFileAsync(字符串文件名、IObservable源、bool覆盖)
{
ThrowIfInvalidFileName(文件名);
var path=path.Combine(_path,filename);
bool fileExists=File.Exists(路径);
使用(var writer=newstreamwriter(路径,!覆盖))
{
var replay=source.replay().RefCount();
如果(覆盖| |!文件存在)
{
var first=等待replay.FirstAsync();
var header=GetCsvHeader(first.GetResults());
等待writer.WriteLineAsync(头);
}
等待replay.ForEachAsync(结果=>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));
}
专用任务RunIVBoardCurrentAdjust(IVBoardAdjustment测试)
{
logger.Info(“加载IV板电流调整测试”);
var testCases=_testCaseLoader.GetIVBoardCurrentAdjustTests().ToArray();
var source=test.RunCurrentAdjustment(测试用例);
返回_fileResultsService.WriteToFileAsync(“IVBoardCurrentAdjust.csv”,source,false);
}
公共IObservable运行电流调整(IEnumerable testCases)
{
返回
测试用例
.选择(运行电流调整)
.Concat();
}
公共IObservable运行电流调整(IVBoardCurrentAdjustTestCase测试用例)
{
调试(“准备IV板电流调整程序”);
返回可观察的。创建(
(观察员,取消令牌)=>
{
var结果=
运行调整(测试用例)
.Do(结果=>logger.Trace(“”)
.选择(
(输出,i)=>
新IVBoardCurrentAdjustTestResults(i、测试用例、输出)
{
Component=“IV板”,
Description=“特征化(割线法)”
})
.Replay();
结果。订阅(观察者、取消令牌);
var task=StoreResultInBTD(结果,testCase,1/testCase.Load);
结果:Connect();
返回任务;
});
}
专用IObservable运行调整(IVBoardAdjustTestCase testCase),其中T:DacCharacterizationSecantInput
{
调试(“初始化IV板测试”);
SetupTest(testCase);
返回
新的dac属性()
.RunSecantMethod(
code=>\u yellowCake.IVBoard.DacRegister.Value=code,
()=>_dmm.Read(),
GetTestInputs(testCase));
}
专用异步任务StoreResultInBTD(IObservable结果,IVBoardAdjustTestCase测试用例,双目标缩放因子=1)
{
var点=
结果
.选择(
结果=>
新IvBoard特性曲线测试点(
(result.Output.Target-result.Output.Error)*targetScalingFactor,
(int)result.Output.Root);
var曲线=等待点。ToArray();
_yellowCake.BoardTest.WriteIVBoardAjust(曲线、testCase.Mode、testCase.Range);
_yellowCake.BoardTest.SaveToFile();
}
私有IEnumerable GetTestInputs(IVBoardAdjustTestCase testCase),其中T:DacCharacterizationSecantInput
{
foreach(testCase.Inputs中的var输入)
{
Debug(“获取下一个测试输入”);
_dmm.Config.PowerLineCycles.Value=input.IntegrationTime;
收益率-收益率-投入;
}
}
公共IObservable RunSecantMethod(
动作设置输出,
Func readMeanOutput,
IEnumerable(可数输入)
{
var search=new SecantSearch();
var param=SecantMethodParameter.Create(setDacOutput,readMeanOutput);
返回
可观察
.创造(
(观察员,取消令牌)=>
Task.Run(()=>
{
foreach(输入中的var输入)
{
cancelToken.ThrowIfCancellationRequested();
var结果=
搜索FindRoot(
param.SearchFunction,
input.FirstGuess,
input.SecondGuess,
输入。目标,
input.SearchOptions,
取消代币,
()=>参数适应账户);
如果(!result.Converged)
{
observer.OnError(新的失败合并异常(结果));
}
OnNext观察员(结果);
}
},取消标记);
}

乍一看,这似乎是Rx与async/await混合的结果。几乎总是有一个简单得多的普通Rx查询来完成这项工作。现在对我来说太晚了。早上我会考虑一下的。@Enigmativity谢谢。这里的问题是,我需要一个用于同步目的的任务,否则我就不知道何时将结果写入文件。我将发布更多的代码来展示这一切是什么样子的(我昨晚也来晚了)。乍一看,这似乎是混合了Rx和async/await的结果。几乎总是有一个简单得多的普通Rx查询来完成这项工作。现在对我来说太晚了。早上我会考虑一下的。@Enigmativity谢谢。问题h