6
\$\begingroup\$

My question is regarding a rather inelegant solution to a problem that I came up with a while ago.

I was making a Winform Application to access active directory among other things and needed to thread my application to stop my UI from freezing.

Although I have little knowledge of actual threading (I kept hitting cross-thread access issues), I've built a little function that allowed me to pass an object, act on it in a separate thread and then once it returned to the UI CurrentContext I could act on it again to update the view.

TL;DR

The below function sends a Generic Item to a separate thread, then once acted upon, returns the item and acts on it again.

public static void doThreadedQuery<T>(Func<T> processQuery, Action<T> onComplete)
{
 TaskScheduler scheduler = TaskScheduler.FromCurrentSynchronizationContext();
 Task<T> doQuery = new Task<T>(processQuery);
 doQuery.ContinueWith((results) => { if (onComplete != null) onComplete(results.Result); }, scheduler);
 doQuery.Start();
}

An example usage for something similar is pass an activeDirectory SearchResultCollection, parse through it and generate a TreeView/Grid. Then return the populated grid and UI updated without freezing.

Now I have done variants with a progress function/cancellation token etc, but I suppose my question is: on a base level, is there some horrible inefficiency or simple equivalent I am unaware of (aside from the Background Worker I am not overly fond of)?

I feel like I'm re-inventing the wheel here.

RubberDuck
31.1k6 gold badges73 silver badges176 bronze badges
asked Jan 6, 2014 at 9:24
\$\endgroup\$

1 Answer 1

5
\$\begingroup\$

You shouldn't usually use Task constructor directly, use Task.Factory.StartNew (or Run).

I also don't understand why you want to have a separate method for starting a task. It is completely superficial. It's just as useless as adding Print method that calls Console.WriteLine instead of using Console.WriteLine directly.

Normally, you just do this:

void SomeUICode ()
{
 Task.Factory.StartNew (() => {
 // This will run on thread pool
 return DoSomeExpensiveWork ();
 }).ContinueWith (result => {
 // This will run on UI thread
 UpdateUI (result);
 }, TaskScheduler.FromCurrentSynchronizationContext ());
}

It's basically the same thing you wrote, but without abstracting it away doThreadedQuery since there's nothing to abstract away here.

However, if you don't mind using C# 5, you can simplify things with async/await operators:

async void SomeUICode ()
{
 var result = await Task.Factory.Run (() => {
 // This will run on thread pool
 return DoSomeExpensiveWork ();
 });
 // The rest of this method will be scheduled on UI thread:
 UpdateUI (result);
}

Note the await keyword that "splits" the method and schedules the rest of it as a continuation on current synchronization context (exactly the same thing you did manually).

I declared the function as async void but you should only do this for event handlers (which have to be void). In all other cases, declare the function as async Task so the calling code has a chance to wait for it to finish:

async Task SomeUICode ()
{
 // ...
}

On a side note, doThreadedQuery is a rather unfortunate name because

Glorfindel
1,1133 gold badges14 silver badges27 bronze badges
answered Jan 6, 2014 at 11:44
\$\endgroup\$
5
  • \$\begingroup\$ I think he was just indicating (by example name) a process running a structured Query language operation. But otherwise, nice examples! \$\endgroup\$ Commented Jan 6, 2014 at 12:25
  • \$\begingroup\$ @Spiked: Perhaps, but his method doesn't really "know" whether function passed to it does a LINQ query or something else, so it's still a bad naming decision. \$\endgroup\$ Commented Jan 6, 2014 at 12:34
  • 1
    \$\begingroup\$ async void should be avoided (in most cases) \$\endgroup\$ Commented Jan 6, 2014 at 12:46
  • \$\begingroup\$ @Dave: Thanks for the remark, I amended my answer. \$\endgroup\$ Commented Jan 6, 2014 at 12:49
  • \$\begingroup\$ I forgot to change the example name when I took it from the project, it was doing an ActiveDirectory query and T was defined hence the name. \$\endgroup\$ Commented Jan 6, 2014 at 17:37

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.