2

I have an application that imports data read from text files from a directory into a database. I have a UI that allows the user to click an import button and begin importing data and when the user clicks on that button again I wanted to stop importing the data in those files. I began using threads to allow this, so that I would not freeze up the UI while data was being imported. But Im having a few issues. I started using thread.Abort() to kill the thread after the user stops wanting to import but when the user clicks import again, some duplicate data is added to the database because it begins reading at the top of the text file which I dont want. I have been told to use ManualResetEvents and Thread.Join() to trigger for the import to finish, but im confused how that is supposed to work. Right now my code looks like this:

    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();

            }


        }
    }

Basically what im trying to do is to keep the tread looping and checking to see if there is any files to be read, if there is then import The Data otherwise sleep for 4 seconds. Should I be using a threading Timer for this? I am a bit unsure of what to do.

c0dej0ckey
  • 23
  • 1
  • 6

2 Answers2

1

Do not, in any way, block the UI thread by calling Thread.Join or ManualResetEvent.WaitOne. This will do exactly what you were trying to prevent; freeze the UI. Instead you need to create the MRE with its state initially set to true. Then in the importData method you need to periodically call WaitOne to see if importing should proceed (when the event is signaled) or pause (when the event is unsignaled).

Here is a rough sketch of how you would call WaitOne inside the importData method. Obviously, you would need to make adjustments to fit it into your specific implementation.

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

Then from your importButton.Click event handler you can call event1.Reset to pause the import operation or event1.Set to resume it.

Also, you should try to avoid calling Thread.Abort at all costs. It usually leads to more problems unless extra-special-nearly-impossible care is taken to avoid corrupting the state of the AppDomain.

Brian Gideon
  • 47,849
  • 13
  • 107
  • 150
0

Use timer for running the import process instead of thread, and define a variable to check if user request to stopinstead of thread.Abort() which by the way should be avoided.

In this code use System.Timers.Timer. and flag AutoReset property to false, so only import data if user not request to stop.

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();
}

As you notice you don't have to use ManualResetEvent here. however if you want to be notified when code is completed or so you can use AutoResetEvent or raise an event for more detailed example check this.

Community
  • 1
  • 1
Jalal Said
  • 15,906
  • 7
  • 45
  • 68