2

Not quite sure what's going on here -

This bit of code is causing issues as it is first called from the Main thread (verified in Task view in VS) and scheduling the tasks, however when setting a breakpoint in UpdateSearchCache we're now in the worker thread - no longer main!

Subsequent pieces of UI code being called from there fail as they're executed on the worker thread.

Isn't that the whole point of specifying the scheduler? What am I missing?

This code is called when starting our app. It's called from the Bootstrapper of our PRISM app and running on the MainThread.

The SynchronizationContext.Current is NOT null when the Task is started.

var currentScheduler = TaskScheduler.FromCurrentSynchronizationContext();
var ctx = SynchronizationContext.Current;
if (ctx == null)
    throw new NullReferenceException();

Task.Factory
    .StartNew(
        () =>
            SearchHelper.CacheSearchResults(_service.GetData())
    .ContinueWith(result => UpdateCache(result.Result), currentScheduler);
svick
  • 236,525
  • 50
  • 385
  • 514
cacau
  • 3,606
  • 3
  • 21
  • 42

2 Answers2

3

TaskScheduler.FromCurrentSynchronizationContext throws an InvalidOperationException when there is no synchronisation context on the calling thread i.e. SynchronizationContext.Current returns null.

Lee
  • 142,018
  • 20
  • 234
  • 287
  • 1
    But how come the method is executed on a worker thread when it's supposed to run in the current thread, a.k.a. main? – cacau Mar 05 '14 at 11:11
  • @cacau - Which thread the continuation is scheduled on depends on which synchronisation context you have. The default synchronisation context schedules tasks to be run on the thread pool. However if you're getting the exception, the problem occurs before you even start the parent task, and there's not enough code here to see why that might happen. – Lee Mar 05 '14 at 12:32
  • This piece of called is called in our (PRISM) bootstrapper right at the startup time of our app. Question edited accordingly. At least in VS we can see a bunch of worker threads plus the main thread exist already at that time.. – cacau Mar 05 '14 at 15:26
  • @cacau: you are aware that SyncronisationContext.Current is not a global variable, it is a per thread property. You might want to capture the TaskScheduler from UI thread code and use it from the backgruond thread. –  Mar 05 '14 at 22:38
  • @jdv-JandeVaan, he does with `TaskScheduler.FromCurrentSynchronizationContext`. That captures the sync. context at the moment of call, and it's non-null. – noseratio Mar 06 '14 at 03:20
1

Something really weird is going on here. Try the following approach. It's a workaround, but it also may help to diagnose the problem:

var dispatcher = Dispatcher.CurrentDispatcher;
Debug.Assert(dispatcher == Application.Current.Dispatcher);

Task.Factory
    .StartNew(
        () => SearchHelper.CacheSearchResults(_service.GetData()))
    .ContinueWith(result => 
    { 
        // is the app's dispatcher still the same?

        Debug.Assert(dispatcher == Application.Current.Dispatcher);

        // explicitly use Dispatcher.BeginInvoke, that's what
        // DispatcherSynchronizationContext does behind the scene

        Application.Current.Dispatcher.BeginInvoke(new Action(
            () => UpdateCache(result.Result)));

    }, TaskContinuationOptions.ExecuteSynchronously);
noseratio
  • 59,932
  • 34
  • 208
  • 486