0

So we have a program that has a pause button in the main UI. When the pause button is pressed a time stamp is saved and when pressed again another timestamp is then created, the time diff between the two is calculated and then then all 3 values along with a user name are sent to a db using WebClient. The problem is that if the button is pressed multiple times rapidly the there are issues with getting all the data sent.

I had the idea to make a queue and instead of the pause button sending the data it would write it to a queue and then have a separate thread check the queue and handle the handle the sending.

Using a timer to fire a background worker every x-seconds for the simple task of reading a queue and sending data if its there feels like overkill/abuse. Am I right and this is more of regular thread kind of job or is the background worker the way to go?

user5999614
  • 147
  • 1
  • 10
  • _"Using a timer ... feels like overkill/abuse"_. "write my own threads" is arguably overkill. For WinForms, a `BackgroundWorker` is your best bet as it is available on .NET 2+. Otherwise use `Task` –  Mar 24 '16 at 00:43

3 Answers3

2

I would use a ConcurrentQueue wrapped in a BlockingCollection class, and use a Task to watch the collection.

var pendingEntries = new BlockingCollection<List<Entry>>(new ConcurrentQueue<List<Entry>>());
var loggingTask = System.Threading.Tasks.Task.Factory.StartNew(ConsumerTask, pendingEntries);

The class Entry is a container for whatever you want to record at once.

The consumer task looks like this:

private void ConsumerTask(object parameter) 
{
    var localPendingEntries = (BlockingCollection<List<Entry>>)parameter;
    foreach (var entry in localPendingEntries.GetConsumingEnumerable())
    {
       // push the 'entry' where you want.
    }
}

To add entries to the queue do this:

var newEntry = new Entry(......);
pendingEntries.Add(newEntry);

When you are done adding entries, call this:

pendingEntries.CompleteAdding();

The task will fall out of the foreach loop once it has finished processing the entries currently in the queue.

You can then wait for the task to complete with:

loggingTask.Wait();
pendingEntries.Dispose();
Dave Tillman
  • 589
  • 4
  • 13
  • I store the data for the pause event in a class Called PauseEvent, Should Entry be my PauseEvent class or is there a premade entry class i have to use? – user5999614 Mar 24 '16 at 13:37
  • I want to add events to the queue on the second click of the pause button. I am only adding one event on each second click of the button so do i just call `pendingEntries.Add()` and `pendingEntries.CompleteAdding()` in the PauseButton_Click() method? The consumer method just goes into the MainForm class right? Where would the PendingEntries and loggingtask definitions go? – user5999614 Mar 24 '16 at 14:01
  • In my example code, Entry can be anything you choose. It sounds like your PauseEvent class could go there. – Dave Tillman Mar 24 '16 at 16:57
  • You only need to call pendingEntries.Add(). CompeteAdding is only used when you want to shut down the task when you are all done. – Dave Tillman Mar 24 '16 at 16:58
  • Put **pendingEntries** and **loggingTask** anywhere you choose. MainForm is probably fine. You can probably find more complete examples using an internet search. – Dave Tillman Mar 24 '16 at 17:00
0

I personally use threads. Backgroundworkers take up too much code for me. If you have the thread as a global variable, you can kill or start the thread at will.

Take a look here: BackgroundWorker vs background Thread

For both a thread and backgroundworker you're able to see if they're running. If they're running then you're able to either disable the button if it's running or make it so the button does nothing. For what you're doing, I recommend background worker.

Community
  • 1
  • 1
  • I am not so much interested in style as I in making sure I am not abusing the background worker. though just not using background worker solves the abuse issue if it was one. – user5999614 Mar 23 '16 at 19:37
  • I don't believe you're abusing it. Backgroundworker should be able to handle everything. –  Mar 23 '16 at 19:40
  • In fact, a BackgroundWorker is also just Thread, so you cannot 'abuse' it. But it's a bit heavier than a plain thread, because it has additional features (report progress event, completed event, ...) out of the box. If you need these features use a BgWorker. If you don't need them use a plain thread. – derpirscher Mar 23 '16 at 20:11
0
protected void btnPause_Click(object sender, EventArgs e)
    {
        System.ComponentModel.BackgroundWorker disableWorker = new System.ComponentModel.BackgroundWorker();
        disableWorker.DoWork += new System.ComponentModel.DoWorkEventHandler(disableWorker_DoWork);
        disableWorker.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(disableWorker_RunWorkerCompleted);
        disableWorker.RunWorkerAsync();
    }

    void disableWorker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
    {
        btnPause.Enabled = true;
    }

    void disableWorker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
    {
        btnPause.Enabled = false;
        System.Threading.Thread.Sleep(10000);            
    }
  • 1
    Consider improving your answer. _[Code-only answers may fall under 'Very Low Quality' ...and are candidates for deletion....We've always touted that we aren't a code factory. We are the people who teach others to fish. Code-only answers only feed a person for a day](http://meta.stackexchange.com/questions/148272/is-there-any-benefit-to-allowing-code-only-answers-while-blocking-code-only-ques)_ –  Mar 24 '16 at 00:54