使用布尔标志无法突破任务-我做错了什么
本文关键字:错了 什么 任务 突破 布尔 标志 | 更新日期: 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; }
}
}
布尔值不会"神奇地"结束一个操作,读/写是同步的,所以除非异常中断它,否则操作将继续,直到它完成。
因此,解决问题的最简单方法是保存打开流的引用,如果必须在操作仍在运行时取消操作,只需关闭并处理它,它就会中断执行(记住捕获异常),文件就可以打开了。
对我来说,正确的答案是执行以下操作:
-
使用任务取消模式,这样就不用:
Task.Run(() => GoOnRecordingFrames());
我有:
_cancellation_token_source = new CancellationTokenSource(); _cancellation_token = _cancellation_token_source.Token; _task = new Task(GoOnRecordingFrames, _cancellation_token); _task.Start();
-
现在我没有设置一个标志来突破循环,而是有了一个标记取消,后面跟着一个
Task.Wait
,这样文件总是在再次打开进行覆盖之前由任务关闭:public void EndRecordingSession() { if (_cancellation_token_source != null) _cancellation_token_source.Cancel(); if (_task != null && _task.Status == TaskStatus.Running) _task.Wait(); }
-
处理循环内的取消:
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); } }