It's not that it does not switch back, it does not start on a UI thread in the first place because you are using System.Threading.Timer, which processes the ticks on thread pool threads, out of WPF context.
You can replace it with DispatcherTimer.
If a System.Timers.Timer is used in a WPF application, it is worth noting that the System.Timers.Timer runs on a different thread then the user interface (UI) thread. In order to access objects on the user interface (UI) thread, it is necessary to post the operation onto the Dispatcher of the user interface (UI) thread using Invoke or BeginInvoke. Reasons for using a DispatcherTimer opposed to a System.Timers.Timer are that the DispatcherTimer runs on the same thread as the Dispatcher and a DispatcherPriority can be set on the DispatcherTimer.
Also, you need to deal with re-entrance, same as for System.Threading.Timer, because a Cpu bound operation could still be processing the previous tick.
using System; using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Threading; namespace WpfApplication1 { public partial class MainWindow : Window { DispatcherTimer timer = new DispatcherTimer(); long currentlyRunningTasksCount; public MainWindow() { InitializeComponent(); Loaded += MainWindow_Loaded; timer.Interval = TimeSpan.FromSeconds(1); timer.Tick += async (s, e) => { // Prevent re-entrance. // Skip the current tick if a previous one is already in processing. if (Interlocked.CompareExchange(ref currentlyRunningTasksCount, 1, 0) != 0) { return; } try { await ProcessTasks(); } finally { Interlocked.Decrement(ref currentlyRunningTasksCount); } }; } private void MainWindow_Loaded(object sender, RoutedEventArgs e) { // This one would crash, ItemsSource requires to be invoked from the UI thread. // ThreadPool.QueueUserWorkItem(o => { listView.Items.Add("started"); }); listView.Items.Add("started"); timer.Start(); } async Task ProcessTasks() { var computed = await Task.Run(() => CpuBoundComputation()); listView.Items.Add(string.Format("tick processed on {0} threads", computed.ToString())); } /// <summary> /// Computes Cpu-bound tasks. From here and downstream, don't try to interact with the UI. /// </summary> /// <returns>Returns the degree of parallelism achieved.</returns> int CpuBoundComputation() { long concurrentWorkers = 0; return Enumerable.Range(0, 1000) .AsParallel() .WithDegreeOfParallelism(Math.Max(1, Environment.ProcessorCount - 1)) .Select(i => { var cur = Interlocked.Increment(ref concurrentWorkers); SimulateExpensiveOne(); Interlocked.Decrement(ref concurrentWorkers); return (int)cur; }) .Max(); } /// <summary> /// Simulate expensive computation. /// </summary> void SimulateExpensiveOne() { // Prevent from optimizing out the unneeded result with GC.KeepAlive(). GC.KeepAlive(Enumerable.Range(0, 1000000).Select(i => (long)i).Sum()); } } }
If you need a precise control on what's happening, you are better off with queueing events and displaying them independently of processing:
using System; using System.Collections.Concurrent; using System.ComponentModel; using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Threading; namespace WpfApplication2 { public partial class MainWindow : Window { DispatcherTimer fastTimer = new DispatcherTimer(); BackgroundProcessing processing = new BackgroundProcessing(); public MainWindow() { InitializeComponent(); processing.Start(); fastTimer.Interval = TimeSpan.FromMilliseconds(10); fastTimer.Tick += Timer_Tick; fastTimer.Start(); } private void Timer_Tick(object sender, EventArgs e) { Notification notification; while ((notification = processing.TryDequeue()) != null) { listView.Items.Add(new { notification.What, notification.HappenedAt, notification.AttributedToATickOf }); } } protected override void OnClosing(CancelEventArgs e) { base.OnClosing(e); processing.Stop(); } } public class Notification { public string What { get; private set; } public DateTime AttributedToATickOf { get; private set; } public DateTime HappenedAt { get; private set; } public Notification(string what, DateTime happenedAt, DateTime attributedToATickOf) { What = what; HappenedAt = happenedAt; AttributedToATickOf = attributedToATickOf; } } public class BackgroundProcessing { /// <summary> /// Different kind of timer, <see cref="System.Threading.Timer"/> /// </summary> Timer preciseTimer; ConcurrentQueue<Notification> notifications = new ConcurrentQueue<Notification>(); public Notification TryDequeue() { Notification token; notifications.TryDequeue(out token); return token; } public void Start() { preciseTimer = new Timer(o => { var attributedToATickOf = DateTime.Now; var r = new Random(); Parallel.ForEach(Enumerable.Range(0, 2), i => { Thread.Sleep(r.Next(10, 5000)); var happenedAt = DateTime.Now; notifications.Enqueue( new Notification("Successfully loaded cpu 100%", happenedAt, attributedToATickOf)); }); }, null, 0, 1000); } public void Stop() { preciseTimer.Change(0, 0); } } }
UPDATE: For Windows Forms you could replace DispatcherTimer with the System.Windows.Forms.Timer in the second code sample.
TaskScheduler.FromCurrentSynchronizationContext()to action? But then I can't use async/await and will have to change context myself with TPL...pairsfield/property? When and where is it set?