背景
有一个我无法控制的外部程序,它每隔8秒将制表符分隔的行写入“PRN”文件(Test_1234.prn)。我的程序的工作是读取该文件并将第n行(写入文件的最后一行)写入一个简单的样本ListView。我还在视图中运行了一个StopWatch,我计划通过检查StopWatch的秒数来使用时钟来驱动PRN的读取。每4秒我会出去读取文件并返回一个DataTable,然后每8秒我会将该DataTable的第n行写入我的View。 PRN确实占据了很多线,但尺寸永远不会超过1mb - 5mb。
问题
我的问题源于这是一个同步操作的事实,虽然它可以工作,但它只能工作几分钟,并且StopWatch表现不规律并调用多行写入我的SamplesView。这最终导致我的_nextLine计数器超前于实际PRN的位置,并且我得到了一个越界异常。
鉴于缺乏线程经验,我不知道从哪里开始使用线程来修复此代码,以便它按照预期的间隔执行它应该做的事情。
由于Samples集合需要每隔一段时间更新一次,我怀疑我需要实现一些后台线程,这个后台线程尊重View在主线程上更新自己的权利给定我读过的内容{{3 }和here。但是,我不知道从哪里开始。
有人可以帮助实施线程解决方案来解决我的具体问题吗?
//Xaml belongs to my CalibrationView.xaml
<StackPanel Orientation="Vertical">
<Label Content="Run Time:" FontSize="16" FontWeight="Bold" Margin="10,0,0,0"/>
<TextBlock Name="ClockTextBlock" Text="{Binding CurrentTime, Mode=TwoWay}"/>
<ListView ItemsSource="{Binding Samples}" SelectedItem="{Binding SelectedSample}">
<ListView.View>
<GridView>
<GridViewColumn Header="Time" Width="70" DisplayMemberBinding="{Binding Time}"/>
</GridView>
</ListView.View>
</ListView>
</StackPanel>
public class CalibrationViewModel : ViewModelBase
{
DispatcherTimer dt = new DispatcherTimer();
Stopwatch stopWatch = new Stopwatch();
private bool _firstTime = true;
private int _nextLine = 1;
private int _sampleCount;
public CalibrationViewModel(Calibration calibration)
{
Samples = new ObservableCollection<StepRecord>();
dt.Tick += dt_Tick;
dt.Interval = new TimeSpan(0, 0, 0, 1);
}
public ObservableCollection<StepRecord> Samples { get; set; }
public DataTable Prn { get; set; }
public String CurrentTime
{
get { return _currentTime; }
set
{
if (_currentTime != value)
{
_currentTime = value;
OnPropertyChanged("CurrentTime");
}
}
}
void dt_Tick(object sender, EventArgs e)
{
if (stopWatch.IsRunning)
{
TimeSpan ts = stopWatch.Elapsed;
CurrentTime = String.Format("{0:00}:{1:00}:{2:00}", ts.Hours, ts.Minutes, ts.Seconds);
if (ts.Seconds % 4 == 0)
{
// Custom parser that reads a Tab Delimited file and returns a DataTable (PRN)
PrnParser parser = new PrnParser();
Prn = parser.PopulateDataTableFromTextFile(@"C:\Users\Path\To\File\Test_1234.prn");
if (_firstTime)
{
_nextLine = Prn.Rows.Count - 1;
_firstTime = false;
}
}
if (ts.Seconds % 8 == 0)
{
WriteLineToSamplesCollection();
}
}
}
private void WriteLineToSamplesCollection()
{
var record = new StepRecord
{
Time = (Prn.Rows[NextLine].Field<String>("Column-2")),
};
CurrentSample = record;
Samples.Insert(0, record);
_nextLine++;
_sampleCount++;
}
}
答案 0 :(得分:1)
以下代码显示了一个非常基本的示例,说明如何使用FileSystemWatcher监视特定文本文件的更改,并在文件发生更改时读取最后一行。由于Changed
事件已经在单独的线程中调用(来自ThreadPool),因此您不需要关心异步,除非您需要在更新UI时调用Dispatcher
。
private FileSystemWatcher fsw;
public MainWindow()
{
InitializeComponent();
fsw = new FileSystemWatcher
{
Path = @"C:\Users\Path\To\File",
Filter = "Test_1234.prn",
NotifyFilter = NotifyFilters.LastWrite
};
fsw.Changed += (o, e) =>
{
var lastLine = File.ReadAllLines(e.FullPath).Last();
Dispatcher.BeginInvoke((Action<string>)HandleChanges, lastLine);
};
fsw.EnableRaisingEvents = true;
}
private void HandleChanges(string lastLine)
{
// update UI here
}
您肯定需要改进此示例,以便提高行读取效率。每次文件更改时都不要读取所有行,而是保持读取位置并从最后保留的位置开始读取。