我有一个应用程序,它将从文本文件中读取的数据从目录导入数据库。我有一个UI,允许用户单击导入按钮并开始导入数据,当用户再次单击该按钮时,我想停止导入这些文件中的数据。我开始使用线程来允许这样做,这样我就不会在导入数据时冻结UI。但我有一些问题。在用户停止想要导入之后我开始使用thread.Abort()来杀死线程,但是当用户再次单击import时,会向数据库添加一些重复数据,因为它开始在文本文件的顶部读取,我不想要。我被告知使用ManualResetEvents和Thread.Join()来触发导入完成,但我很困惑应该如何工作。现在我的代码看起来像这样:
public ManualResetEvent event1 = new ManualResetEvent(false);
public Thread workerThread;
public Form1
{
InitializeComponent();
}
private void importButton_Click(object sender, EventArgs e)
{
if(importButton.Text == "Begin Import")
{
importButton.Text = "Stop Import";
//runs a service that begins reading and importing data and writing to
//a "console" box.
Service service = new Service(consoleBox);
//run the new thread to begin importing data
workerThread = new Thread(service.importData);
workerThread.Start();
}
else
{
importButton.Text = "Begin Import";
event1.Set();
while(!event1.WaitOne(TimeSpan.FromSeconds(4)))
{ //imports data for 30 more text files
service.importData(30);
workerThread.Join();
}
}
}
基本上我试图做的是保持步骤循环并检查是否有任何文件要读取,如果有则导入数据否则睡眠4秒。我应该使用线程计时器吗?我有点不确定该怎么做。
答案 0 :(得分:1)
不要以任何方式通过调用Thread.Join
或ManualResetEvent.WaitOne
来阻止UI线程。这将完全符合您的预防要求;冻结用户界面。相反,您需要创建最初设置为true
的状态的MRE。然后在importData
方法中,您需要定期调用WaitOne
以查看是否应继续导入(当事件发出信号时)或暂停(事件无信号时)。
以下是如何在WaitOne
方法中调用importData
的草图。显然,您需要进行调整以使其适合您的具体实施。
private void importData()
{
foreach (string filePath in GetFilesInSomeDirectory())
{
event1.WaitOne(); // Block when the MRE is unsignaled.
}
}
然后,您可以通过importButton.Click
事件处理程序调用{{1}}暂停导入操作,或event1.Reset
恢复导入操作。
此外,您应该尽量避免不惜一切代价调用event1.Set
。它通常会导致更多问题,除非采取额外特殊的 - 几乎不可能的关注来避免破坏AppDomain的状态。
答案 1 :(得分:0)
使用计时器来运行导入进程而不是线程,并定义一个变量来检查是否应该避免用户请求停止而不是thread.Abort()
。
在此代码中使用System.Timers.Timer
。并将AutoReset
属性标记为false,因此仅在用户未请求停止时才导入数据。
private System.Timers.Timer _importTimer = new System.Timers.Timer();
private volatile bool _requestStopImport = false;
public Form1()
{
InitializeComponent();
_importTimer.Interval = 4000;//4 seconds
_importTimer.AutoReset = false;//not automatically raise elapse event each time interval elapsed, only if we don't want to stop.
_importTimer.Elapsed += OnImportTimerElapced;
}
private void importButton_Click(object sender, EventArgs e)
{
if (importButton.Text == "Begin Import")
{
importButton.Text = "Stop Import";
StartImport();
}
else
{
importButton.Text = "Begin Import";
StopImport();
}
}
private void OnImportTimerElapced(object sender, System.Timers.TimerEventArgs e)
{
//runs a service that begins reading and importing data and writing to
//a "console" box.
Service service = new Service(consoleBox);//or maybe this would be a class level variable
service.importData();
if (!_requestStopImport)
{
_importTimer.Start();
}
}
private void StartImport()
{
_requestStopImport = false;
_importTimer.Start();
}
private void StopImport()
{
_requestStopImport = true;
_importTimer.Stop();
}
正如您所注意到的,您不必在此使用ManualResetEvent
。但是,如果您希望在代码完成后通知,那么您可以使用AutoResetEvent
或举办活动以获得更详细的示例检查this。