2011-07-17 27 views
2

我有一个应用程序将从文本文件从目录读取到数据库中的数据。我有一个用户界面,允许用户点击导入按钮并开始导入数据,当用户再次点击该按钮时,我想停止导入这些文件中的数据。我开始使用线程来允许这样做,以便在导入数据时不会冻结UI。但我有几个问题。我开始使用thread.Abort()在用户停止导入后终止线程,但是当用户再次单击导入时,一些重复数据被添加到数据库,因为它开始读取文本文件的顶部,我不想要。我被告知使用ManualResetEvents和Thread.Join()来触发导入完成,但我很困惑,应该如何工作。现在我的代码如下所示:c#线程与手动重置事件

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

使用定时器运行导入过程而不是线程,并定义一个变量来检查用户是否要求停止而不是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

1

不要以任何方式通过调用Thread.JoinManualResetEvent.WaitOne来阻止UI线程。这将做你正在试图阻止的事情;冻结用户界面。相反,您需要创建最初设置为true的状态的MRE。然后在importData方法,你需要定期调用WaitOne,看是否进口应继续(当信号的情况下)或暂停(当事件是unsignaled)。

下面是如何在importData方法中调用WaitOne的粗略草图。显然,您需要进行调整以适应您的具体实施。

private void importData() 
{ 
    foreach (string filePath in GetFilesInSomeDirectory()) 
    { 
    event1.WaitOne(); // Block when the MRE is unsignaled. 
    } 
} 

然后从你的importButton.Click事件处理程序可以调用event1.Reset暂停导入操作或event1.Set恢复它。

此外,你应该尽量避免不惜一切代价调用Thread.Abort。通常,除非格外特别 - 几乎不可能的小心,以避免损坏的AppDomain的状态会导致更多的问题。