0

I am trying to launch a thread when an application starts and wait for UI to give it some work without using BackgroundWorker. This thread sleeps when no work is given, and wakes up when the ui asks it do something.

More details:

Simple WPF App: I have a StorageClass to copy files to long term storage. This class is part of the WPF application. When a user clicks a button to store the file to long term storage(Low speed array), I want a thread to copy this file from a highspeed storage array to long term storage. These are large files and I dont want the UI to be blocked. I would like to use one thread that waits for instruction to transfer. Hope this gives more clarity on what I am trying to do.

isakavis
  • 773
  • 1
  • 12
  • 29

1 Answers1

2

If you are using .NET 4.0 or above, I'd recommend the TPL over threading directly, and I'd recommend using a BlockingCollection<T> as a way that the UI can "give some work" via the collection for the consumer to do.

The consumer can then be a long-running Task (again, from the TPL) that consumes from the BlockingCollection<T>, in this way, you don't need any sleep or manual artifacts like that, the BlockingCollection<T> lets you block on a wait for a given period of time, then resume once an item is ready to consume.

So you could define a cancellation token source, blocking collection, and task as:

    private BlockingCollection<Transaction> _bin = new BlockingCollection<Transaction>();
    private CancellationTokenSource _tokenSource = new CancellationTokenSource();
    private Task _consumer;

And your consumer method could be defined as:

    private void ConsumeTransactions()
    {
        // loop until consumer marked completed, or cancellation token set
        while (!_bin.IsCompleted && !_tokenSource.Token.IsCancelRequested)
        {
            Transaction item;

            // try to take item for 100 ms, or until cancelled 
            if (_bin.TryTake(out item, 100, _tokenSource.Token)
            {
                // consume the item
            }
        }
    }

Then you'd fire off the task when your form loads by doing:

// when you have a task running for life of your program, make sure you
// use TaskCreationOptions.LongRunning.  This typically sets up its own
// dedicated thread (not pooled) without having to deal with threads directly
_consumer = Task.Factory.StartNew(ConsumeTransactions, _tokenSource.Token, 
                                  TaskCreationOptions.LongRunning, TaskScheduler.Default);

And then add items by doing:

        _bin.TryAdd(someTransaction);

Where Transaction is just whatever you define your unit of work to perform...

Then, finally, when your application wants to shut down, it can do:

_bin.CompleteAdding();

Which tells the consumer that no more items will ever be added to the queue, this will make the TryTake() return false, and exit the loop since _bin.IsCompleted will then be true.

Your long running task can then loop on the blocking get until the cancellation token (also TPL) is set to tell it to shut down...

James Michael Hare
  • 37,767
  • 9
  • 73
  • 83
  • Excited I am working on based on your jumpt start. – isakavis Jul 25 '12 at 16:43
  • It's worth mentioning that using a `BackgroundWorker` instead of `Tasks` would result in almost identical code. You would simply be adding the `ConsumeTransactions` method as an event handler to `DoWork` instead of using `Task.StartNew` to begin running it in the background. – Servy Jul 25 '12 at 16:55
  • @Servy: True, just mentioned the `Task`s because I'm more a server guy. The main point being that he can use the `BlockingCollection` so that he doesn't have to "spin" or "sleep" his thread/task, he can just attempt to take and it does the rest. – James Michael Hare Jul 25 '12 at 16:57
  • @JamesMichaelHare My point is simply that this is a good approach, and it will work regardless of the mechanism that you use to run some code in a non-UI thread. You can explicitly new up a thread and start that and it's practically the same. The answer is more universal than just a `Task` based approach. (That's not a bad thing, it's a good thing.) – Servy Jul 25 '12 at 16:59
  • James and all if I used backgroundworker thread: what happens, if we have multiple instructions to the background worker thread to transfer before it finished the first instruction? Wont it spawn multiple threads? The idea is I want this to be queued for a single thread to address it. With that said is Backgroundworker thread a good choice? I am diving deep into James' solution because of my concern with multiple request coming before the first request is complete – isakavis Jul 25 '12 at 17:07
  • @user1549435 you could have the BackgrounWorker consume from the BlockingCollection. If you are thinking the GUI will be queueing up several work items before the previous ones finish, then I'd definitely go with the BlockingCollection either being consumed by a Task, or BackgroundWorker. – James Michael Hare Jul 25 '12 at 18:47
  • @user1549435 - Really the best part of BGW is the notification interface, which is easy to create in other ways. TPL is nice because it gives you a lot of control to chain and nest tasks. Personally, I prefer the TPL, myself, but then again I'm more of a server programmer than a client programmer. – James Michael Hare Jul 25 '12 at 18:51
  • James: I went with TPL. There is no need to notify users in the scenario. One thing I am wondering if the long running task be running at low priority or high priority by default. I want this to be lowest priority. – isakavis Jul 25 '12 at 19:37
  • James: One more clarification. Can I suspend the task on network issue and continue upon network available? I used the .NET network class to determine connectivity. – isakavis Jul 25 '12 at 21:12
  • @user1549435: That logic would be largely your own. You can have the process periodically check for network connectivity and then just skip work for a period. – James Michael Hare Jul 26 '12 at 13:52