带手动复位事件的c#线程

带手动复位事件的c#线程,c#,multithreading,forms,C#,Multithreading,Forms,我有一个应用程序,可以将从目录中的文本文件读取的数据导入数据库。我有一个UI,允许用户单击导入按钮并开始导入数据,当用户再次单击该按钮时,我希望停止导入这些文件中的数据。我开始使用线程来实现这一点,以便在导入数据时不会冻结UI。但是我有一些问题。我开始使用thread.Abort()在用户停止导入后终止线程,但当用户再次单击导入时,一些重复数据会添加到数据库中,因为它开始读取我不想要的文本文件的顶部。我被告知要使用ManualResetEvents和Thread.Join()触发以完成导入,但我

我有一个应用程序,可以将从目录中的文本文件读取的数据导入数据库。我有一个UI,允许用户单击导入按钮并开始导入数据,当用户再次单击该按钮时,我希望停止导入这些文件中的数据。我开始使用线程来实现这一点,以便在导入数据时不会冻结UI。但是我有一些问题。我开始使用thread.Abort()在用户停止导入后终止线程,但当用户再次单击导入时,一些重复数据会添加到数据库中,因为它开始读取我不想要的文本文件的顶部。我被告知要使用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秒钟。我应该为此使用线程计时器吗?我有点不确定该怎么办。

使用timer来运行导入过程,而不是线程,并定义一个变量来检查用户是否请求停止,而不是
thread.Abort()

在此代码中使用
System.Timers.Timer
。并将
AutoReset
property标记为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
或引发事件以进行更详细的示例检查。

无论如何,不要通过调用
thread.Join
ManualResetEvent.WaitOne
来阻止UI线程。这正是你想要阻止的;冻结用户界面。相反,您需要创建初始状态设置为
true
的MRE。然后在
importData
方法中,您需要定期调用
WaitOne
,查看导入是否应继续(当事件发出信号时)或暂停(当事件未发出信号时)

下面是如何在
importData
方法中调用
WaitOne
的大致示意图。显然,您需要进行调整以使其适合您的具体实现

private void importData()
{
  foreach (string filePath in GetFilesInSomeDirectory())
  {
    event1.WaitOne(); // Block when the MRE is unsignaled.
  }
}
然后从
导入按钮。单击
事件处理程序,您可以调用
event1.Reset
暂停导入操作,或
event1.Set
继续导入操作

此外,您应该尽一切努力避免调用
Thread.Abort
。它通常会导致更多的问题,除非采取特别的几乎不可能的措施来避免损坏AppDomain的状态