c#带手动重置事件的线程

时间:2011-07-17 02:22:08

标签: c# multithreading forms

我有一个应用程序,它将从文本文件中读取的数据从目录导入数据库。我有一个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秒。我应该使用线程计时器吗?我有点不确定该怎么做。

2 个答案:

答案 0 :(得分:1)

不要以任何方式通过调用Thread.JoinManualResetEvent.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