GUI froze while Dispatcher.BeginInvoke or Task.StartNew - c#

NOTE: Depends on this quetion
I have a view-model like this:
public class ViewModel {
private readonly IPersonService _personService;
private readonly ObservableCollection<SearchPersonModel> _foundedList;
private readonly DispatcherTimer _timer;
private readonly Dispatcher _dispatcher;
private CancellationTokenSource _tokenSource;
public SearchPatientViewModel(IPersonService personService) {
_personService = personService;
_foundedList = new ObservableCollection<SearchPersonModel>();
_dispatcher = (/*CurrentApplication*/).Dispatcher;
_timer = new DispatcherTimer(
TimeSpan.FromMilliseconds(1000),
DispatcherPriority.Normal,
TimerCallBack,
_dispatcher);
_tokenSource = new CancellationTokenSource();
}
public string Term {
get { return _term; }
set {
// implementing INotifyPropertyChanged
if(_term== value)
return;
_term= value;
OnPropertyChanged(() => Term);
tokenSource.Cancel(); // canceling prev search query
_timer.Stop(); // stop the timer to reset it
// start it again to do a search query if user change not the term for 1000ms
_timer.Start();
}
}
private void TimerCallBack(object sender, EventArgs e) {
_timer.Stop();
_tokenSource = new CancellationTokenSource();
var task = Task<IEnumerable<SearchPersonModel>>.Factory
.StartNew(Search, _tokenSource.Token);
_dispatcher.BeginInvoke((Action)(() => {
_foundedList.Clear();
foreach(var item in task.Result)
_foundedList.Add(item);
}), DispatcherPriority.Background);
}
private IEnumerable<SearchPersonModel> Search() {
return _personService.DoSearch(this.Term);
}
}
and in the IPersonService implementation I do this:
public class PersonService : IPersonService {
public IEnumerable<SearchPersonModel> DoSearch(string term){
System.Threading.Thread.Sleep(10000);
return some-search-result;
}
}
However, I expect that while search query is executing, GUI be free. But it froze! Have you any idea where is my mistake?

The problem is that evaluating task.Result will block until the query has completed.
The simplest option is probably to make the Search method perform the _dispatcher.BeginInvoke call at the end instead.
Another option - which will become easier with C# 5 - would be to add a continuation to the task, so that when it's completed you can update the UI. At the moment you'd use Task.ContinueWith; with C# 5 you'd use async and await.

Related

Updating Winforms Label with Timer and Thread, stock app

Gist of it has probably been asked before, but I'm completely lost so I'm looking for some personal guidance. Been trying to make a stock tracker app for funsies using WinForms and the Yahoo API. Trying to get it so you can input a tracker symbol and it will make a new Label that will keep updating itself every so often. However, it keeps giving me error messages about "Cross-thread operation not valid". I've tried to do some googling, but yeah, completely lost. Here is most of the code, hope you guys can make some sense of it.
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using YahooFinanceApi;
namespace stockpoging4
{
public partial class Form1 : Form
{
public Form1()
{
System.Globalization.CultureInfo.DefaultThreadCurrentUICulture = System.Globalization.CultureInfo.GetCultureInfo("en-US");
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
using (Prompt prompt = new Prompt("Enter the ticker symbol", "Add ticker"))
{
string result = prompt.Result;
result = result.ToUpper();
if (!string.IsNullOrEmpty(result))
{
do_Things(result);
}
}
}
public async Task<string> getStockPrices(string symbol)
{
try
{
var securities = await Yahoo.Symbols(symbol).Fields(Field.RegularMarketPrice).QueryAsync();
var aapl = securities[symbol];
var price = aapl[Field.RegularMarketPrice];
return symbol + " $" + price;
}
catch
{
return "404";
}
}
public async void do_Things(string result)
{
string price;
Label label = null;
if (label == null)
{
price = await getStockPrices(result);
label = new Label() { Name = result, Text = result + " $" + price };
flowLayoutPanel2.Controls.Add(label);
}
else
{
Thread testThread = new Thread(async delegate ()
{
uiLockingTask();
price = await getStockPrices(result);
label.Text = result + " $" + price;
label.Update();
});
}
System.Timers.Timer timer = new System.Timers.Timer(10000);
timer.Start();
timer.Elapsed += do_Things(results);
}
private void uiLockingTask() {
Thread.Sleep(5000);
}
}
}
Let me point out several things in your implementation.
You subscribe to timer.Elapsed after timer.Start that might be invalid in case of a short-timer interval
The event handler is called in background that's why you continuously get "Cross-thread operation not valid". UI components should be dispatched correctly from background threads, for example, by calling flowLayoutPanel2.BeginInvoke(new Action(() => flowLayoutPanel2.Controls.Add(label))); and label.BeginInvoke(new Action(label.Update)). This change already would fix your exception.
Despite the fact that I would implement this functionality in a different way, here I post slightly changed code that just does exactly what you need with some tweaks.
public partial class Form1 : Form
{
Task _runningTask;
CancellationTokenSource _cancellationToken;
public Form1()
{
System.Globalization.CultureInfo.DefaultThreadCurrentUICulture = System.Globalization.CultureInfo.GetCultureInfo("en-US");
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
using (Prompt prompt = new Prompt("Enter the ticker symbol", "Add ticker"))
{
string result = prompt.Result;
result = result.ToUpper();
if (!string.IsNullOrEmpty(result))
{
do_Things(result);
_cancellationToken = new CancellationTokenSource();
_runningTask = StartTimer(() => do_Things(result), _cancellationToken);
}
}
}
private void onCancelClick()
{
_cancellationToken.Cancel();
}
public async Task<string> getStockPrices(string symbol)
{
try
{
var securities = await Yahoo.Symbols(symbol).Fields(Field.RegularMarketPrice).QueryAsync();
var aapl = securities[symbol];
var price = aapl[Field.RegularMarketPrice];
return symbol + " $" + price;
}
catch
{
return "404";
}
}
private async Task StartTimer(Action action, CancellationTokenSource cancellationTokenSource)
{
try
{
while (!cancellationTokenSource.IsCancellationRequested)
{
await Task.Delay(1000, cancellationTokenSource.Token);
action();
}
}
catch (OperationCanceledException) { }
}
public async void do_Things(string result)
{
var price = await getStockPrices(result);
var label = new Label() { Name = result, Text = result + " $" + price };
flowLayoutPanel2.BeginInvoke(new Action(() => flowLayoutPanel2.Controls.Add(label)));
}
}
A much easier way is using async these days.
Here is a class which triggers an Action every interval:
public class UITimer : IDisposable
{
private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
// use a private function which returns a task
private async Task Innerloop(TimeSpan interval, Action<UITimer> action)
{
try
{
while (!_cancellationTokenSource.IsCancellationRequested)
{
await Task.Delay(interval, _cancellationTokenSource.Token);
action(this);
}
}
catch (OperationCanceledException) { }
}
// the constructor calls the private StartTimer, (the first part will run synchroniously, until the away delay)
public UITimer(TimeSpan interval, Action<UITimer> action) =>
_ = Innerloop(interval, action);
// make sure the while loop will stop.
public void Dispose() =>
_cancellationTokenSource?.Cancel();
}
If you work with dotnet 3.0 or higher, you can use the IAsyncDisposable. With this you're able to await the DisposeAsync method, so you can await the _timerTask to be finished.
And I created a new form with this as code behind:
public partial class Form1 : Form
{
private readonly UITimer _uiTimer;
private int _counter;
public Form1()
{
InitializeComponent();
// setup the time and pass the callback action
_uiTimer = new UITimer(TimeSpan.FromSeconds(1), Update);
}
// the orgin timer is passed as parameter.
private void Update(UITimer timer)
{
// do your thing on the UI thread.
_counter++;
label1.Text= _counter.ToString();
}
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
// make sure the time (whileloop) is stopped.
_uiTimer.Dispose();
}
}
The advantage is, that the callback runs on the UI thread but doesn't block it. The await Task.Delay(..) is using a Timer in the background, but posts the rest of the method/statemachine on the UI thread (because the UI thread has a SynchronizaionContext)
Easy but does the trick ;-)

Reporting Progress from Tasks

Is there a way to know the status of individual Task while using the WhenAll() or WaitAll() methods of Task.
When I use await Task.WhenAll(tasks.ToArray()) for every task that completes I would like to know that the task has completed.
To report progress from an async or concurrent process use the IProgress<T> abstraction. Using the Progress<T> implmentation you can easily capture the current context and run your progress update / completion expression on the ui context while doing your work in the background:
public class WorkItem {
public async Task DoWork(IProgress<bool> completionNotification) {
await Task.Delay(TimeSpan.FromSeconds(2));
//Work Done
completionNotification.Report(true);
}
//Get, Set, Fire Property change etc
public bool Completed {
get; set;
}
}
public class ViewModel {
public async void ButtonClickHandler(object sender, EventArgs e) {
var workitemTasks = WorkItems.Select(workItem =>
workItem.DoWork(new Progress<bool>(done => workItem.Completed = done)))
.ToList();
await Task.WhenAll(workitemTasks);
}
//Get, Set, Fire Property change etc
public IEnumerable<WorkItem> WorkItems {
get; set;
}
}
More Info
Reporting Progress From Async Tasks #StephenCleary
There is probably a much nicer way to do this, but here is a raw class which takes an array of unstarted tasks and fires an event each time some of the tasks completes execution:
public class TaskBatchRunner
{
private Task[] _tasks;
public event EventHandler<Task> TaskCompleted;
public TaskBatchRunner(Task[] tasks)
{
_tasks = tasks.Select(t =>
new Task(() =>
{
t.ContinueWith(OnTaskCompleted);
t.Start();
})).ToArray();
}
public void Run()
{
foreach (var t in _tasks) t.Start();
Task.WaitAll(_tasks);
}
private void OnTaskCompleted(Task completedTask)
{
TaskCompleted?.Invoke(this, completedTask);
}
}
Usage:
var taskRunner = new TaskBatchRunner(tasks.ToArray());
taskRunner.TaskCompleted += MyTaskCompleted;
taskRunner.Run();
...
private void MyTaskCompleted(object sender, Task e)
{
System.Diagnostics.Debug.WriteLine($"task {e.Id} completed!");
}

C# (WPF) Async Thread with interface to GUI

thanks for reading this topic.
For a new WPF application (build in C#) I have a question regarding the design.
The past few days I have read a lot about Async programming in C# (based on .NET 4.5).
What we would like to do is: Create a new async thread, which does independent background tasks. When this thread has data available: then send this data to the main program (by an public interface). So, the thread will set data in the main program and immediately return to the thread again. The main program will raise an event (INotifyPropertyChanged) when data has been changed.
What will be the best way to create this Async thread? Or at least, what would be the best way to design this feature?
At the moment I have build an application which creates a thread.
This does not work Async at the moment:
public MainWindow()
{
InitializeComponent();
InitGuiInterface(this);
//Create thread
new OuterLabel_Thread(this);
}
And the class "OuterLabel_Thread.cs"here below:
public class OuterLabel_Thread
{
private MainWindow context = null;
private bool exit = false;
private int count = 0;
public OuterLabel_Thread(MainWindow context)
{
this.context = context;
Console.WriteLine("Running sample thread");
Thread thread = new Thread(delegate ()
{
Console.WriteLine("Sample thread started");
//start new task
//run();
Task.Factory.StartNew(run);
});
thread.Start();
}
public void Exit()
{
exit = true;
}
private void run()
{
while (!exit)
{
DateTime Time1 = DateTime.Now;
if (context != null && context.GuiInterface != null)
{
//context.GuiInterface.UpdateThreadCount(count, "label_code_content");
}
Console.WriteLine("Background thread count = " + count);
count++;
if (count > 1000)
{
exit = true;
}
//Console.WriteLine((DateTime.Now - Time1).TotalMilliseconds.ToString());
Thread.Sleep(10);
}
}
}
Many thanks in advance!
Kind regards,
Rein.
as you want to keep the thread alive and as far as I understand, you don't know exactly when or if you will reach the 1000 mark, async might be the wrong choice. Correct me if i'm wrong.
For your case I would recommend using the BackgroundWorker:
private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
int count = 0;
BackgroundWorker worker = sender as BackgroundWorker;
while (!exit)
{
DateTime Time1 = DateTime.Now;
worker.ReportProgress(count);
count++;
if (count > 1000)
{
exit = true;
}
Thread.Sleep(10);
}
}
// This event handler updates the progress.
private void BackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
resultLabel.Text = ("Background thread count = " + e.ProgressPercentage.ToString());
}
private void BackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled == true)
{
resultLabel.Text = "Canceled!";
}
else if (e.Error != null)
{
resultLabel.Text = "Error: " + e.Error.Message;
}
else
{
resultLabel.Text = "Done!";
}
}
The best way would be using async+await and tasks.
private async void LaunchButton_OnClick(object sender, RoutedEventArgs e)
{
resultLabel.Content = "Task running";
resultLabel.Content = await SomeLongRunningTaskAsync();
}
private Task<string> SomeLongRunningTaskAsync()
{
return Task.Run(
() =>
{
// Put your background work in here. with Task.Run it's not going to run on UI
int count = 0;
while (count < 1000)
{
count++;
Thread.Sleep(10);
}
return "Task done";
});
}
I can't figure out if you are looking for a service or a long running task.
Since the others have good examples of long running tasks I've made a Service
It uses some advanced concpets like SynchronizationContext that you should read up on before using this in production code. Google async await and Stephen Cleary.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var foo = new FooService();
foo.StartService(); // UI thrad calling
}
}
public class FooService
{
private SynchronizationContext _context;
private CancellationTokenSource _cts;
private CancellationToken _token;
private Task _task;
public void StartService()
{
_context = SynchronizationContext.Current; // Depends on the UI thread being the one to start the service or this will fail
_cts = new CancellationTokenSource(10000); // Run for 10 seconds
_token = _cts.Token;
_task = Task.Run(() => Run(), _token);
}
public async Task Stop()
{
_cts.Cancel();
await _task; // wait for task to finish
}
private void Run()
{
while (!_token.IsCancellationRequested)
{
// Do work
Thread.Sleep(1000);
// Alternative use Control.Invoke() if you have access to a UI element, to delegate to the UI thread
_context.Send((id) => Console.WriteLine($"Delegate from thread {id} to thread {Thread.CurrentThread.ManagedThreadId}"), Thread.CurrentThread.ManagedThreadId);
}
}
}

async await not working with Timer

I have a Presence monitor class which is used to detect users active/inactive status. That class has a timer in its Start method which called on application start:
public class PresenceMonitor
{
private volatile bool _running;
private Timer _timer;
private readonly TimeSpan _presenceCheckInterval = TimeSpan.FromMinutes(1);
public PresenceMonitor()
{
}
public void Start()
{
// Start the timer
_timer = new Timer(_ =>
{
Check();
}, null, TimeSpan.Zero, _presenceCheckInterval);
}
private void Check()
{
if (_running)
{
return;
}
_running = true;
// Dowork
}
}
The "Check" method is fired after every one minute. That piece of code is working fine but now my "Do work" methods have become async await so I had to change this Presence Monitor class to something like this:
public class PresenceMonitor
{
private volatile bool _running;
private Timer _timer;
private readonly TimeSpan _presenceCheckInterval = TimeSpan.FromMinutes(1);
public PresenceMonitor()
{
}
public void Start()
{
// Start the timer
var timer = new System.Threading.Timer(async (e) =>
{
await CheckAsync();
}, null, TimeSpan.Zero, _presenceCheckInterval);
}
private async Task CheckAsync()
{
if (_running)
{
return;
}
_running = true;
// await DoworkAsync
}
}
Unfortunately "CheckAsync" method now is getting fired once only instead of every minute. Can you tell me what I am doing wrong here to call async await after regular intervals?
Is there any correct way to do the same?
You could consider creating an event and handler to handle the timer ticks and then invoke your check.
public class PresenceMonitor {
private volatile bool _running;
private Timer timer;
private readonly TimeSpan _presenceCheckInterval = TimeSpan.FromMinutes(1);
public PresenceMonitor() {
Tick += OnTick;
}
public void Start() {
if (_running) {
return; //already running
}
// Start the timer
timer = new System.Threading.Timer(_ => {
Tick(this, EventArgs.Empty);//rasie event
}, null, TimeSpan.Zero, _presenceCheckInterval);
}
private event EventHandler Tick = delegate { };
private async void OnTick(object sender, EventArgs args) {
if (_running) {
return;
}
_running = true;
await DoworkAsync();
}
private Task DoworkAsync() {
//...
}
}
If I understand correctly your requirements, you can get rid of timer and use asynchronous loop.
But you need make Start method asynchronous too
public class PresenceMonitor
{
private volatile bool _running; // possible not needed "volatile" anymore
private readonly int _presenceCheckInterval = 60000; // Milliseconds
public PresenceMonitor()
{
}
public async Task Start()
{
while (true) // may be use some "exit" logic
{
await CheckAsync();
await Task.Delay(_presenceCheckInterval)
}
}
private async Task CheckAsync()
{
if (_running)
{
return;
}
_running = true;
// await DoworkAsync
}
}
Then you can start monitoring
var monitor = new PresenceMonitor();
await monitor.Start();
You can even start monitoring in synchronous way
var monitor = new PresenceMonitor();
monitor.Start(); // Will start monitoring
But approach above is "dangerous" in the way, that any exception thrown inside CheckAsync method will not be propagated. When you start using async-await be ready to "convert" whole application to support it.

Thread.Interrupt to stop long sleep at app shutdown - Is there a better approach

I'm having a small background thread which runs for the applications lifetime - however when the application is shutdown, the thread should exit gracefully.
The problem is that the thread runs some code at an interval of 15 minutes - which means it sleeps ALOT.
Now in order to get it out of sleep, I toss an interrupt at it - my question is however, if there's a better approach to this, since interrupts generate ThreadInterruptedException.
Here's the gist of my code (somewhat pseudo):
public class BackgroundUpdater : IDisposable
{
private Thread myThread;
private const int intervalTime = 900000; // 15 minutes
public void Dispose()
{
myThread.Interrupt();
}
public void Start()
{
myThread = new Thread(ThreadedWork);
myThread.IsBackground = true; // To ensure against app waiting for thread to exit
myThread.Priority = ThreadPriority.BelowNormal;
myThread.Start();
}
private void ThreadedWork()
{
try
{
while (true)
{
Thread.Sleep(900000); // 15 minutes
DoWork();
}
}
catch (ThreadInterruptedException)
{
}
}
}
There's absolutely a better way - either use Monitor.Wait/Pulse instead of Sleep/Interrupt, or use an Auto/ManualResetEvent. (You'd probably want a ManualResetEvent in this case.)
Personally I'm a Wait/Pulse fan, probably due to it being like Java's wait()/notify() mechanism. However, there are definitely times where reset events are more useful.
Your code would look something like this:
private readonly object padlock = new object();
private volatile bool stopping = false;
public void Stop() // Could make this Dispose if you want
{
stopping = true;
lock (padlock)
{
Monitor.Pulse(padlock);
}
}
private void ThreadedWork()
{
while (!stopping)
{
DoWork();
lock (padlock)
{
Monitor.Wait(padlock, TimeSpan.FromMinutes(15));
}
}
}
For more details, see my threading tutorial, in particular the pages on deadlocks, waiting and pulsing, the page on wait handles. Joe Albahari also has a tutorial which covers the same topics and compares them.
I haven't looked in detail yet, but I wouldn't be surprised if Parallel Extensions also had some functionality to make this easier.
You could use an Event to Check if the Process should end like this:
var eventX = new AutoResetEvent(false);
while (true)
{
if(eventX.WaitOne(900000, false))
{
break;
}
DoWork();
}
There is CancellationTokenSource class in .NET 4 and later which simplifies this task a bit.
private readonly CancellationTokenSource cancellationTokenSource =
new CancellationTokenSource();
private void Run()
{
while (!cancellationTokenSource.IsCancellationRequested)
{
DoWork();
cancellationTokenSource.Token.WaitHandle.WaitOne(
TimeSpan.FromMinutes(15));
}
}
public void Stop()
{
cancellationTokenSource.Cancel();
}
Don't forget that CancellationTokenSource is disposable, so make sure you dispose it properly.
One method might be to add a cancel event or delegate that the thread will subscribe to. When the cancel event is invoke, the thread can stop itself.
I absolutely like Jon Skeets answer. However, this might be a bit easier to understand and should also work:
public class BackgroundTask : IDisposable
{
private readonly CancellationTokenSource cancellationTokenSource;
private bool stop;
public BackgroundTask()
{
this.cancellationTokenSource = new CancellationTokenSource();
this.stop = false;
}
public void Stop()
{
this.stop = true;
this.cancellationTokenSource.Cancel();
}
public void Dispose()
{
this.cancellationTokenSource.Dispose();
}
private void ThreadedWork(object state)
{
using (var syncHandle = new ManualResetEventSlim())
{
while (!this.stop)
{
syncHandle.Wait(TimeSpan.FromMinutes(15), this.cancellationTokenSource.Token);
if (!this.cancellationTokenSource.IsCancellationRequested)
{
// DoWork();
}
}
}
}
}
Or, including waiting for the background task to actually have stopped (in this case, Dispose must be invoked by other thread than the one the background thread is running on, and of course this is not perfect code, it requires the worker thread to actually have started):
using System;
using System.Threading;
public class BackgroundTask : IDisposable
{
private readonly ManualResetEventSlim threadedWorkEndSyncHandle;
private readonly CancellationTokenSource cancellationTokenSource;
private bool stop;
public BackgroundTask()
{
this.threadedWorkEndSyncHandle = new ManualResetEventSlim();
this.cancellationTokenSource = new CancellationTokenSource();
this.stop = false;
}
public void Dispose()
{
this.stop = true;
this.cancellationTokenSource.Cancel();
this.threadedWorkEndSyncHandle.Wait();
this.cancellationTokenSource.Dispose();
this.threadedWorkEndSyncHandle.Dispose();
}
private void ThreadedWork(object state)
{
try
{
using (var syncHandle = new ManualResetEventSlim())
{
while (!this.stop)
{
syncHandle.Wait(TimeSpan.FromMinutes(15), this.cancellationTokenSource.Token);
if (!this.cancellationTokenSource.IsCancellationRequested)
{
// DoWork();
}
}
}
}
finally
{
this.threadedWorkEndSyncHandle.Set();
}
}
}
If you see any flaws and disadvantages over Jon Skeets solution i'd like to hear them as i always enjoy learning ;-)
I guess this is slower and uses more memory and should thus not be used in a large scale and short timeframe. Any other?

Categories