0

I know I'm missing something stupid, the "StartProcess" Method is making the UI unresponsive and no amount of googling and tutorials has led me to an answer.

Here is my code:

    public MainWindow()
    {
        InitializeComponent();
        txtBlock.Text = "Testing";
        Initialize();
    }

    public void Initialize()
    {
        uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();

        StartProcess();
    }

    async void StartProcess()
    {
         Task<int> result = Task.Factory.StartNew(() =>
        {
            txtBlock.Text += ("\n starting updater");
            while (true)
            {
                Thread.Sleep(3000);
            }
            return 0;
        }, CancellationToken.None, TaskCreationOptions.LongRunning, uiScheduler);
    }

Some background: I'm building an app that has to poll the DB every 5mins and update the UI with a to-do list for the user, hence the while(true) loop. The app has to poll the DB continuously throughout its lifetime.

Swifty
  • 1,422
  • 2
  • 18
  • 38
  • You could try using `Task.Delay` instead of `Thread.Sleep`. At the moment you seem to be calling `Thread.Sleep` on the UI thread which will make your UI unresponsive. Also, simply making your method `async` doesn't perform magic. – Daniel Kelley Sep 10 '14 at 15:17
  • You should look at [Rx](http://stackoverflow.com/tags/system.reactive/info "system.reactive"). – Paulo Morgado Sep 10 '14 at 16:08
  • This answer may be USEFUL https://stackoverflow.com/questions/4253088/updating-gui-wpf-using-a-different-thread – yu yang Jian Jun 25 '20 at 13:38

1 Answers1

6

Well, you asked the TPL to invoke the Func in UI thread, it obeys your words.

In StartNew you pass uiScheduler as TaskScheduler, so the task will be queued to Dispatcher which will be invoked by UI thread.

If you don't want to execute in UI thread then use TaskScheduler.Default, doing so you can't update txtBlock.Text inside the Func. You need to marshal the call to UI thread or just set the txtBlock.Text outside the Func before StartNew.

Always prefer Task.Run if you're in .Net 4.5, be aware that StartNew is dangerous.

Couple of things to note:

  • Note that you're not leveraging the async feature at all. Without an await keyword method is not at all async. You don't need the async keyword at all(Infact you'll get a compiler warning, please pay attention to what compiler says).
  • Avoid async void methods(Except in event handlers), always use async Task if you don't need any return value, so that you could await it in the caller or attach continuation and so forth.

Update to add some code:

async Task StartProcess()
{
    while (true)
    {
        var result = await Task.Run(()=> MethodWhichCallsDBAndReturnsSomething());
        txtBlock.Text = result.ToString();//Use result and update UI
        await Task.Delay(5 * 60 * 1000);//await 5 mins
    }
}
Sriram Sakthivel
  • 72,067
  • 7
  • 111
  • 189
  • Regarding your last line - they'd then need to marshal any calls to update the UI back to the UI thread (which you no doubt know but maybe the OP doesn't). – Daniel Kelley Sep 10 '14 at 15:20
  • @DanielKelley I was updating my answer. You're too fast :) – Sriram Sakthivel Sep 10 '14 at 15:22
  • Thanks sriram, very informative. How would I go about incorporating the await keyword in a task that is suppose to run perpetually? My goal is to kick off a worker thread that would keep polling the db every 5mins and updating the ui if relevant data is returned. – Swifty Sep 10 '14 at 21:35
  • @Swifty Updated my answer, see if that helps. If you need more help drop a comment. – Sriram Sakthivel Sep 10 '14 at 21:46
  • Thanks, wouldn't the ui update code produce an error since the ui update request is not coming from the STA thread? – Swifty Sep 10 '14 at 22:50
  • @Sriram I see what you did there. The While runs on the UI thread, but the await keywords both triggers new threads allowing the UI thread to continue. I'm wondering if it wouldn't be more efficient to move the whole StartProcess to a new thread and passing the UI update requests the Dispatcher? – Swifty Sep 11 '14 at 06:31
  • 1
    @Swifty This will be more efficient IMO. `await Task.Delay` isn't creating any thread. It just sets up a timer internally. So technically we're not wasting any thread. Also in `await Task.Run` we're not necessarily creating a new thread(We are using a thread which already exist in threadpool). If you want to completely create a new thread and just pass the UI work to dispatcher take a look at my recent answer and scott's answer [here](http://stackoverflow.com/questions/25745176/how-to-provide-a-feedback-to-ui-in-a-async-method). `IProgress` is the thing you'll be using. – Sriram Sakthivel Sep 11 '14 at 09:23