0

Is there a way to solve this error " The calling thread cannot access this object because a different thread owns it" without using dispatcher because dispatcher causes freezing on UI when the codes has a longer time to process is there another way to do it? without causing the freeze on the UI

bRaNdOn
  • 1,060
  • 4
  • 17
  • 37
  • 2
    Let the "longer time process" run on a background/worker thread - not on the UI thread. Only if the result of this has been finally computed, invoke the UI through the dispatcher to assign the result to the appropriate UI element(s). –  Jun 30 '14 at 04:13
  • Thank you the advice will try to apply in on my solution – bRaNdOn Jun 30 '14 at 04:19
  • Can you provide my some sample code for this kind of approach?? – bRaNdOn Jun 30 '14 at 04:34
  • What approach? From your question i deduce that you already know how to use the Dispatcher, or am i mistaken? –  Jun 30 '14 at 04:35
  • 1
    What version of .NET are you using? If you are using 4.0+, I would highly recommend utilizing the `async`/`await` pattern to separate background tasks and UI related code. If you are on 4.0, there is the `Bcl.Async` NuGet package from Microsoft that adds this functionality, where as it is baked into 4.5+. – Erik Jun 30 '14 at 04:44

2 Answers2

8

No, you have to update the UIElement on the UI thread as the error says.

Yes, there other ways to run something on the UI thread other than using the Dispatcher, but they like the Dispatcher still run whatever it is you want to run on the UI thread - so will still freeze the UI.

If you are using C# 5 and .NET 4.5 or above you can easily run your long running process without blocking the UI Thread then when it completes continue on the UI thread (without worrying about how it works) using the async and await keywords:

    private async Task<string> SimLongRunningProcessAsync()
    {
        await Task.Delay(2000);
        return "Success";
    }

    private async void Button_Click(object sender, RoutedEventArgs e)
    {
        button.Content = "Running...";
        var result = await SimLongRunningProcessAsync();
        button.Content = result;
    }

If you do not have those however you will want to use the Dispatcher. The Dispatcher actually assists you running processes without freezing the UI

What you want to do is run the long running processes off the UI thread then when it is finished update the UI - which the Dispatcher helps you to:

  1. Start long process on another thread and return immediately.
  2. (long process still running, UI continues to update)
  3. Long process finishes, update the UI on the UI thread.

e.g.:

    private void UpdateButtonContent(string text)
    {
        button.Content = text;
    }

    private void SimLongRunningProcess()
    {
        Thread.Sleep(2000);
    }

    private void OnProcessFinished(Task task)
    {
        string content;
        if(task.Exception != null)
        {
            content = task.Exception.Message;
        }
        else
        {
            content = "Success";
        }

        Dispatcher.BeginInvoke(new Action<string>(UpdateButtonContent), DispatcherPriority.Normal, content);
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        // Start long running process and return immediatly
        var task = Task.Factory.StartNew(SimLongRunningProcess);
        task.ContinueWith(OnProcessFinished);
    }
markmnl
  • 11,116
  • 8
  • 73
  • 109
  • 1
    The first example does not work because the Button_Click method is not async, therefore the await command cannot be used. – Jim Yarbro Aug 18 '17 at 10:08
  • @markmnl Your response with the use of `.NET 4.5 or later` is great. You solved my similar issue. Thank you for sharing your knowledge (and thus helping others). – nam Jul 24 '19 at 02:16
1

If you are using .NET 4.0 or newer, you can utilize the async and await keywords for clean, asynchronous code. This feature is built into 4.5 directly, and for 4.0 there is a NuGet package available from Microsoft (Microsoft.Bcl.Async) to enable this functionality.

For example, a user clicks a button and you wish to do something that may take a bit of time, then report back to the user.

// Notice how this method is marked 'async', this allows the 'await' keyword
async void OnButtonClicked(object sender, EventArgs e)
{
   var button = sender as Button;

   button.IsEnabled = false;
   button.Text = "Calculating...";

   int result = await DoSomeWork();

   button.IsEnabled = true;
   button.Text = "Calculate";
}

// This is an asynchronous method that returns an integer result
Task<int> DoSomeWork()
{
   // Do some lengthy computation here, will be run on a background thread
   return 42;
}

Alternatively, you could use other mechanisms like the BackgroundWorker class, or the Asynchronous Programming Model (APM) with callbacks. These are fine, however the async/await pattern is preferable.

If you have a choice, target .NET 4.5 (Win7+). If you must also support Windows XP, target .NET 4.0 and use the NuGet packages for Microsoft.Bcl.Async.

Erik
  • 12,730
  • 5
  • 36
  • 42
  • The async and await keywords do not exist in .Net 4.0. They don't appear until .Net 4.5+ (can't press the bloody 'enter' key by itself to start a new line on this edit!) Sorry. Missed that detail about the NuGet package... but that won't be available Express editions. :( – Richard Robertson Jul 11 '15 at 18:19
  • I was using async event handlers and same error. My problem solved after I removed `.ConfigureAwait(false)` after the awaitable calls (e.g `int result = await DoSomeWork().ConfigureAwait(false);`. – Alisson Reinaldo Silva Jul 14 '16 at 00:41