使用布尔标志无法突破任务-我做错了什么

本文关键字:错了 什么 任务 突破 布尔 标志 | 更新日期: 2023-09-27 18:29:24

我有一个类AcquisitionWriter,它定期将接收到的数据写入文件。它由另一个类ClientClass使用,该类具有Reset方法,该方法应停止当前采集并覆盖文件,同时从那时起保持记录。这是通过调用writer.FinishSection(),然后调用writer.OpenFile(filename)writer.Start()来完成的。

问题是:FinishSection似乎并没有停止编写操作来继续,所以当我调用OpenFile(filename)时,文件已经打开了。

我尝试过将布尔标志设为volatile,但这似乎不是问题所在。我怀疑GoOnWritingFrames任务在本应停止的情况下仍在运行,或者文件没有正确关闭,或者可能是其他原因。

internal class ClientClass
{
    AcquisitionWriter _writer = new AcquisitionWriter();

    protected override void Start()
    {
        _writer.OpenFile();
        _writer.StartRecordingSession();
    }
    protected override void Pause()
    {
        _writer.EndRecordingSession();
    }
    protected override void Reset()   
    {
        /// HERE IS THE PROBLEM:
        /// EndRecordingSession seems not to be enough to prevent 
        /// _writer.GoOnRecordingFrames() to keep going,
        /// so "OpenFile()" gives IOError because the file is still/already open
        _writer.EndRecordingSession();
        _writer.OpenFile();
        _writer.StartRecordingSession();
    }
    protected override void Finish()
    {
        _writer.EndRecordingSession();
    }


    public void ProcessarFrame(object sender, Frame frame)
    {            
        _writer.AdicionarFrame(frame);
    }
}

public class AcquisitionWriter
{
    private string _file_path = SomewhereElse.getFilePath();

    Queue<Frame> _frames = new Queue<Frame>();
    private readonly object _lockObj = new object();
    // two flags to sinalize a new file cannot (yet) be open
    private volatile bool _recording = false;
    private volatile bool _finishing_recording = false;


    public void OpenFile()
    {
        using (var stream = new FileStream(_file_path, FileMode.Create, FileAccess.Write))
        using (var writer = new BinaryWriter(stream))
        {
            var headerBytes = SomewhereElse.getHeaderBytes();
            writer.Write(headerBytes);
            writer.Write(SomewhereElse.GROUP_SEPARATOR);
        }
    }

    public void StartRecordingSession()
    {
        ConfigureRecordingSession();
        _recording = true;
        Task.Run(() => GoOnRecordingFrames());
    }

    private void ConfigureRecordingSession()
    {
        using (var stream = new FileStream(_file_path, FileMode.Append, FileAccess.Write))
        using (var writer = new BinaryWriter(stream))
        {
            int elapsed = Convert.ToInt32((DateTime.UtcNow - SomewhereElse.StartTime).TotalMilliseconds);
            writer.Write(elapsed);
            var ActiveIndices = SomewhereElse.getActiveIndices();
            byte numberOfActiveSensors = (byte)(ActiveIndices.Count());
            writer.Write(numberOfActiveSensors);
            byte[] indicesOfActiveChannels = ActiveIndices.Select(i => Convert.ToByte(i))
                                                          .ToArray();
            writer.Write(indicesOfActiveChannels);
        }
    }

    private void GoOnRecordingFrames()
    {
        bool shouldContinue = true;
        while (shouldContinue)
        {
            using (FileStream stream = new FileStream(_file_path, FileMode.Append, FileAccess.Write))
            using (BinaryWriter writer = new BinaryWriter(stream))
            {
                while (_frames.Count > 0)
                {
                    Frame frame;
                    lock (_lockObj)
                    {
                        frame = _frames.Dequeue();
                    }
                    writer.Write(frame.Serialize());
                }
                if (!_recording)
                {
                    writer.Write(SomewhereElse.END_SEPARATOR); // need to write this only upon exit
                    _finishing_recording = true;
                    shouldContinue = false;
                }
            }
            Thread.Sleep(100);
        }
        _finishing_recording = false;
    }

    public void AddFrame(Frame frame)
    {
        lock (_lockObj)
        {
            _frames.Enqueue(frame);
        }
    }

    public void EndRecordingSession()
    {
        _recording = false;
        while (_finishing_recording) 
        {
            ;
        }
        int x = 0;
    }

    public bool Recording
    {
        get { return _recording; }
    }

}

使用布尔标志无法突破任务-我做错了什么

布尔值不会"神奇地"结束一个操作,读/写是同步的,所以除非异常中断它,否则操作将继续,直到它完成。

因此,解决问题的最简单方法是保存打开流的引用,如果必须在操作仍在运行时取消操作,只需关闭并处理它,它就会中断执行(记住捕获异常),文件就可以打开了。

对我来说,正确的答案是执行以下操作:

  1. 使用任务取消模式,这样就不用:

    Task.Run(() => GoOnRecordingFrames());
    

    我有:

    _cancellation_token_source = new CancellationTokenSource();
    _cancellation_token = _cancellation_token_source.Token;
    _task = new Task(GoOnRecordingFrames, _cancellation_token);
    _task.Start();
    
  2. 现在我没有设置一个标志来突破循环,而是有了一个标记取消,后面跟着一个Task.Wait,这样文件总是在再次打开进行覆盖之前由任务关闭:

    public void EndRecordingSession()
    {
        if (_cancellation_token_source != null)
            _cancellation_token_source.Cancel();
        if (_task != null && _task.Status == TaskStatus.Running)
            _task.Wait();
    }
    
  3. 处理循环内的取消:

    private void GoOnRecordingFrames()
    {
        bool continuar = true;
        while (continuar)
        {
            using (var stream = new FileStream(CaminhoArquivo, FileMode.Append, FileAccess.Write))
            using (var writer = new BinaryWriter(stream))
            {
                while (_frames.Count > 0)
                {
                    Frame frame;
                    lock (_lockObj)
                    {
                        frame = _frames.Dequeue();
                    }
                    writer.Write(frame.Serializar());
                }
                ////// Handling Cancellation using the Token!
                if (_cancellation_token.IsCancellationRequested)
                {
                    writer.Write(Separadores.END_SEPARATOR);
                    continuar = false;
                }
            }
            Thread.Sleep(100);
        }
    }