3

I do not see the answer yet, although I have looked on stackoverflow and documentation.

This error is coming up over the ContinueWidth below if I call the code that creates the tasks from a timer handler or within another task. For example, if it is wrapped in another task that creates tasks every interval (e.g. every 1 second) as follows. The only reason why I have the high level task is to create these lower tasks every interval.

//Higher level task creation

...

Task task = Task.Factory.StartNew(new Action(UpdateAllDuringInterval));

...

    private void UpdateAllDuringInterval()
    {
        Stopwatch stopWatch = new Stopwatch();

        do
        {
            // Start the stopwatch
            stopWatch.Start();

            // Create tasks
            List<Task> tasks = CreateTasksAndStart();

            // Wait for the tasks to complete if testing, since want to check results
            if (this._testMode)
            {
                Task[] taskArray = tasks.ToArray();
                Task.WaitAll(taskArray);
            }

            if (!_testMode)
            {
                // Get remaining time to complete interval and sleep for that time
                int remainingTimeInMilliseconds = this._pollingIntervalInMilliseconds -
                                                  (int) stopWatch.Elapsed.TotalMilliseconds;
                    // truncating milliseconds
                if (remainingTimeInMilliseconds > 0)
                    Thread.Sleep(remainingTimeInMilliseconds);
                        // will give time to CPU. Note that using Sleep used to be frowned upon but no longer due to advantages in multitasksing/CPU utilization
            }
        } while (!_testMode); // continue updating stocks once per interval if not testing
    }

    private List<Task> CreateTasksAndStart()
    {
        var tasks = new List<Task>();

        lock (syncLock)
        {
            for (int i = 0; i < _stocksToUpdate.Count; i++)
            {
                var item = _stocksToUpdate[i];
                var initialTask = Task.Factory.StartNew(() =>
                {
                    GetUpdatedStockInformation(item);
                });
                var continuationTask = initialTask.ContinueWith((antecendent) =>
                {
                    UpdateUIWithUpdatedStockInformation();
                }
                , CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion,
                TaskScheduler.FromCurrentSynchronizationContext());

                // For testing, we want to wait on the continuation task to make sure updates are done
                tasks.Add(continuationTask);
            }
        }
        return tasks;
    }

If more information is needed, just let me know. Feel free to give me other pitfalls of this code as well. Thanks!

dcastro
  • 66,540
  • 21
  • 145
  • 155
Buck
  • 599
  • 7
  • 20

1 Answers1

8

The cause is simple however the solution may require you to redesign some of the interactions of your tasks. You do not have a SynchronizationContext set (i.e. SynchronizationContext.Current is null), and therefore a TaskScheduler cannot be created from the it. One option is to call TaskScheduler.FromCurrentSynchronizationContext() in a place where you are running on the UI thread and then pass in down to the method where the continuations are constructed. Another is create the tasks an pass them up so that the UI thread can attach the continuations.

It is generally considered a code smell to have flags like _testMode that, presumably, only test code sets. Then you've got some interaction which you are not really testing because the test code does one thing, but the actual application does another.

Mike Zboray
  • 39,828
  • 3
  • 90
  • 122
  • Mike - thanks for the solution. It passed through the methods well and worked like a charm - many thanks! Your other feedback was also appreciated. I need to think about a better solution for that. – Buck Jan 27 '14 at 02:04
  • I am observing that SynchronizationContext.Current is null even for console application type of project. Why would that be? At least it has a console output UI which can be updated from a thread-pool thread which is a background thread in nature. – RBT Feb 28 '16 at 05:07
  • 1
    @Rasik All applications start with a null synchronization context. Typically there's some framework you are using that sets it up for you (windows forms, wpf, asp.net, etc). Standard console applications don't have any kind of framework that sets that for you, nor do they require one as you've mentioned. – Mike Zboray Feb 28 '16 at 05:27
  • Thanks a lot. Save my day!! – Emdadul Sawon Jan 12 '17 at 09:24