I have an intense computation I'm doing, which includes code run in parallel. Inside the parallel methods we await calls to async methods. Because Parallel.For can't do that, we have some code based on channels.
The problem is that it seems to be blocking the UI thread, even though we're setting up the handler to avoid that. If I use Task.Delay(1) in the worker it seems to work, but that's only curing the symptom and not the problem.
How can I keep the UI thread from getting blocked?
Here's the code to the view model:
using Prism.Commands;
using Prism.Mvvm;
using Extensions.ParallelAsync;
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace MVVMAwaitUiThread
{
public class MainWindowViewModel : BindableBase
{
public MainWindowViewModel()
{
DoSomethingGoodCommand = new DelegateCommand(DoSomethingGood);
DoSomethingBadCommand = new DelegateCommand(DoSomethingBad);
}
private ProgressViewModel _progressViewModel;
public ProgressViewModel ProgressViewModel
{
get => _progressViewModel;
set => SetProperty(ref _progressViewModel, value);
}
private bool _isBusy = false;
public bool IsBusy
{
get => _isBusy;
set => SetProperty(ref _isBusy, value);
}
private string _workText = "";
public string WorkText
{
get => _workText;
set => SetProperty(ref _workText, value);
}
public DelegateCommand DoSomethingGoodCommand { get; private set; }
public async void DoSomethingGood()
{
IsBusy = true;
try
{
ProgressViewModel = new ProgressViewModel();
double sum = await ReallyDoSomething(1, ProgressViewModel.Progress, ProgressViewModel.CancellationToken);
WorkText = $"Did work {DateTime.Now} -> {sum}.";
}
catch (OperationCanceledException)
{
// do nothing
}
finally
{
IsBusy = false;
}
}
public DelegateCommand DoSomethingBadCommand { get; private set; }
public async void DoSomethingBad()
{
IsBusy = true;
try
{
ProgressViewModel = new ProgressViewModel();
double sum = await ReallyDoSomething(0, ProgressViewModel.Progress, ProgressViewModel.CancellationToken);
WorkText = $"Did work {DateTime.Now} -> {sum}.";
}
catch (OperationCanceledException)
{
// do nothing
}
finally
{
IsBusy = false;
}
}
/// <summary>
/// Calling this with 0 doesn't work, but 1 does
/// </summary>
private async Task<double> ReallyDoSomething(int delay, IProgress<double> progress, CancellationToken cancellationToken)
{
const double maxIterations = 250;
const int sampleCount = 10;
const int maxDegreeOfParallelism = -1; // this doesn't seem to have any effect
const double totalIterations = sampleCount * maxIterations;
int completedIterations = 0;
ConcurrentBag<double> bag = new ConcurrentBag<double>();
// In reality, I have calculations that make calls to async/await methods, but each iteration can be parallel
// Can't make async calls in parallel.for, so this is what we have come up with
await ParallelChannelsAsync.ForAsync(0, sampleCount, maxDegreeOfParallelism, cancellationToken, Eval).ConfigureAwait(false);
async Task Eval(int seed, CancellationToken cancellationToken)
{
double sum = seed;
for (int i = 0; i < maxIterations; ++i)
{
sum += i * (i + 1.0); // simulate computation
await Task.Delay(delay); // simulate an async call
Interlocked.Increment(ref completedIterations);
progress?.Report(completedIterations / totalIterations);
cancellationToken.ThrowIfCancellationRequested();
}
bag.Add(sum / maxIterations);
};
return bag.Sum();
}
}
}
This is a (very-)simplified VS2019 project which demonstrates the problem:
https://drive.google.com/file/d/1ZB4r6QRu94hbxkz_qblkVQiQZCiNLN9i/view?usp=sharing
It's unclear what your code actually does but just because a method has a asynchronous API doesn't necessarily means that it is implemented to not block.
Consider the following method as an example. It appears to be async from a callers perspective but it clearly isn't:
public Task<int> MyNotSoAsyncMethod()
{
//I'm actually blocking...
Thread.Sleep(3000);
return Task.FromResult(0);
}
How can I keep the UI thread from getting blocked?
If you want to be sure not to block the calling thread regardless of the implementation of the method you are calling, then call it on a background thread. For example by creating a Task:
await Task.Run(DoSomethingThatMightBlock);
Using info from #JonasH and #mm8 and a lot of debugging, the problem is really that the UI thread is being starved by events fired from INotifyPropertyChanged.PropertyChanged because we're using MVVM.
We used two pieces to solve the problem. Where the main algorithm is called, we did need to use a Task.Run() to get it on a separate thread.
But we also had a lot of calls to IProgress.Report(), and the UI was actually doing more work than it needed to. In general, if you get multiple Reports() before the UI can draw it, then you really only need the last one. So we wrote this code to basically throw away Report() calls that are 'queued' up.
/// <summary>
/// Assuming a busy workload during progress reports, it's valuable to only keep the next most recent
/// progress value, rather than back pressuring the application with tons of out-dated progress values,
/// which can result in a locked up application.
/// </summary>
/// <typeparam name="T">Type of value to report.</typeparam>
public class ThrottledProgress<T> : IProgress<T>, IAsyncDisposable
{
private readonly IProgress<T> _progress;
private readonly Channel<T> _channel;
private Task _completion;
public ThrottledProgress(Action<T> handleReport)
{
_progress = new Progress<T>(handleReport);
_channel = Channel.CreateBounded<T>(new BoundedChannelOptions(1)
{
AllowSynchronousContinuations = false,
FullMode = BoundedChannelFullMode.DropOldest,
SingleReader = true,
SingleWriter = true
});
_completion = ConsumeAsync();
}
private async Task ConsumeAsync()
{
await foreach (T value in _channel.Reader.ReadAllAsync().ConfigureAwait(false))
{
_progress.Report(value);
}
}
void IProgress<T>.Report(T value)
{
_channel.Writer.TryWrite(value);
}
public async ValueTask DisposeAsync()
{
if (_completion is object)
{
_channel.Writer.TryComplete();
await _completion.ConfigureAwait(false);
_completion = null;
}
}
}
Related
Background:
I have an application I am developing that deals with a large number of addons for another application. One if its primary uses is to safely modify file records in files with fewer records so that they may be treated as one file (almost as if it is combing the files together into one set of records. To do this safely it keeps track of vital information about those files and changes made to them so that those changes can be undone if they don't work as expected.
When my application starts, it analyzes those files and keeps essential properties in a cache (to reduce load times). If a file is missing from the cache, the most important stuff is retrieved and then a background worker must process the file for more information. If a file that was previously modified has been updated with a new version of the file, the UI must confirm this with the user and its modification data removed. All of this information, including information on its modification is stored in the cache.
My Problem:
My problem is that neither of these processes are guaranteed to run (the confirmation window or the background file processor). If either of them run, then the cache must be updated by the main thread. I don't know enough about worker threads, and which thread runs the BackgroundWorker.RunWorkerCompleted event handler in order to effectively decide how to approach guaranteeing that the cache updater is run after either (or both) processes are completed.
To sum up: if either process is run, they both must finish and (potentially) wait for the other to be completed before running the cache update code. How can I do this?
ADJUNCT INFO (My current intervention that doesn't seem to work very well):
I have a line in the RunWorkerCompleted handler that waits until the form reference is null before continuing and exiting but maybe this was a mistake as it sometimes locks my program up.
SpinWait.SpinUntil(() => overwriteForm == null);
I haven't included any more code because I anticipate that this is more of a conceptual question than a code one. However, if necessary, I can supply code if it helps.
I think CountDownTask is what you need
using System;
using System.Threading;
public class Program
{
public class AtomicInteger
{
protected int value = 0;
public AtomicInteger(int value)
{
this.value = value;
}
public int DecrementAndGet()
{
int answer = Interlocked.Decrement(ref value);
return answer;
}
}
public interface Runnable
{
void Run();
}
public class CountDownTask
{
private AtomicInteger count;
private Runnable task;
private Object lk = new Object();
private volatile bool runnable;
private bool cancelled;
public CountDownTask(Int32 count, Runnable task)
{
this.count = new AtomicInteger(count);
this.task = task;
this.runnable = false;
this.cancelled = false;
}
public void CountDown()
{
if (count.DecrementAndGet() == 0)
{
lock (lk)
{
runnable = true;
Monitor.Pulse(lk);
}
}
}
public void Await()
{
lock (lk)
{
while (!runnable)
{
Monitor.Wait(lk);
}
if (cancelled)
{
Console.WriteLine("Sorry! I was cancelled");
}
else {
task.Run();
}
}
}
public void Cancel()
{
lock (lk)
{
runnable = true;
cancelled = true;
Monitor.Pulse(lk);
}
}
}
public class HelloWorldTask : Runnable
{
public void Run()
{
Console.WriteLine("Hello World, I'm last one");
}
}
public static void Main()
{
Thread.CurrentThread.Name = "Main";
Console.WriteLine("Current Thread: " + Thread.CurrentThread.Name);
CountDownTask countDownTask = new CountDownTask(3, new HelloWorldTask());
Thread worker1 = new Thread(() => {
Console.WriteLine("Worker 1 run");
countDownTask.CountDown();
});
Thread worker2 = new Thread(() => {
Console.WriteLine("Worker 2 run");
countDownTask.CountDown();
});
Thread lastThread = new Thread(() => countDownTask.Await());
lastThread.Start();
worker1.Start();
worker2.Start();
//countDownTask.Cancel();
Console.WriteLine("Main Thread Run");
countDownTask.CountDown();
Thread.Sleep(1000);
}
}
let me explain (but you can refer Java CountDownLatch)
1. To ensure a task must run after another tasks, we need create a Wait function to wait for they done, so I used
while(!runnable) {
Monitor.Wait(lk);
}
2. When there is a task done, we need count down, and if count down to zero (it means all of the tasks was done) we will need notify to blocked thread to wake up and process task
if(count.decrementAndGet() == 0) {
lock(lk) {
runnable = true;
Monitor.Pulse(lk);
}
}
Let read more about volatile, thanks
While dung ta van's "CountDownTask" answer isn't quite what I needed, it heavily inspired the solution below (see it for more info). Basically all I did was add some extra functionality and most importantly: made it so that each task "vote" on the outcome (true or false). Thanks dung ta van!
To be fair, dung ta van's solution DOES work to guarantee execution which as it turns out isn't quite what I needed. My solution adds the ability to make that execution conditional.
This was my solution which worked:
public enum PendingBool
{
Unknown = -1,
False,
True
}
public interface IRunnableTask
{
void Run();
}
public class AtomicInteger
{
int integer;
public int Value { get { return integer; } }
public AtomicInteger(int value) { integer = value; }
public int Decrement() { return Interlocked.Decrement(ref integer); }
public static implicit operator int(AtomicInteger ai) { return ai.integer; }
}
public class TaskElectionEventArgs
{
public bool VoteResult { get; private set; }
public TaskElectionEventArgs(bool vote) { VoteResult = vote; }
}
public delegate void VoteEventHandler(object sender, TaskElectionEventArgs e);
public class SingleVoteTask
{
private AtomicInteger votesLeft;
private IRunnableTask task;
private volatile bool runTask = false;
private object _lock = new object();
public event VoteEventHandler VoteCast;
public event VoteEventHandler TaskCompleted;
public bool IsWaiting { get { return votesLeft.Value > 0; } }
public PendingBool Result
{
get
{
if (votesLeft > 0)
return PendingBool.Unknown;
else if (runTask)
return PendingBool.True;
else
return PendingBool.False;
}
}
public SingleVoteTask(int numberOfVotes, IRunnableTask taskToRun)
{
votesLeft = new AtomicInteger(numberOfVotes);
task = taskToRun;
}
public void CastVote(bool vote)
{
votesLeft.Decrement();
runTask |= vote;
VoteCast?.Invoke(this, new TaskElectionEventArgs(vote));
if (votesLeft == 0)
lock (_lock)
{
Monitor.Pulse(_lock);
}
}
public void Await()
{
lock(_lock)
{
while (votesLeft > 0)
Monitor.Wait(_lock);
if (runTask)
task.Run();
TaskCompleted?.Invoke(this, new TaskElectionEventArgs(runTask));
}
}
}
Implementing the above solution was as simple as creating the SingleVoteTask in the UI thread and then having each thread affecting the outcome cast a vote.
I would like to run tasks in parallel, with no more than 10 instances running at a given time.
This is the code I have so far:
private void Listen()
{
while (true)
{
var context = listener.GetContext();
var task = Task.Run(() => HandleContextAsync(context));
Interlocked.Increment(ref countTask);
if (countTask > 10)
{
//I save tasks in the collection
}
else
{
task.ContinueWith(delegate { Interlocked.Decrement(ref countTask); }); //I accomplish the task and reduce the counter
}
}
}
I would suggest that you use a Parallel loop; for example:
Parallel.For(1, 10, a =>
{
var context = listener.GetContext();
...
});
That will start a defined number of tasks without you needing to manage the process yourself.
If you want to continually execute code in parallel, with up to 10 instances at a time, this may be worth considering:
private void Listen()
{
var options = new ParallelOptions() { MaxDegreeOfParallelism = 10 };
Parallel.For(1, long.MaxValue - 1, options, (i) =>
{
var context = listener.GetContext();
HandleContextAsync(context);
});
}
Basically, it will run the code continually (well roughly long.MaxValue times). MaxDegreeOfParallelism ensures that it runs only 10 'instances' of the code at a time.
I'm assuming that the result from GetContext is not created by you, so, its probably not useful to use a Parallel.For when you don't know how many times to run or don't have all the contexts to handle right away.
So, probably the best way to resolve this would be by implementing your own TaskScheduler. This way you can add more tasks to be resolved on demand with a fixed concurrency level.
Based on the example from Microsoft Docs website you can already achieve this.
I made an example program with some changes to the LimitedConcurrencyLevelTaskScheduler from the website.
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace parallel
{
class Program
{
private static Random Rand = new Random();
static void Main(string[] args)
{
var ts = new LimitedConcurrencyLevelTaskScheduler(10);
var taskFactory = new TaskFactory(ts);
while (true)
{
var context = GetContext(ts);
if (context.Equals("Q", StringComparison.OrdinalIgnoreCase))
break;
taskFactory.StartNew(() => HandleContextAsync(context));
}
Console.WriteLine("Waiting...");
while (ts.CountRunning != 0)
{
Console.WriteLine("Now running {0}x tasks with {1}x queued.", ts.CountRunning, ts.CountQueued);
Thread.Yield();
Thread.Sleep(100);
}
}
private static void HandleContextAsync(string context)
{
// delays for 1-10 seconds to make the example easier to understand
Thread.Sleep(Rand.Next(1000, 10000));
Console.WriteLine("Context: {0}, from thread: {1}", context, Thread.CurrentThread.ManagedThreadId);
}
private static string GetContext(LimitedConcurrencyLevelTaskScheduler ts)
{
Console.WriteLine("Now running {0}x tasks with {1}x queued.", ts.CountRunning, ts.CountQueued);
return Console.ReadLine();
}
}
// Provides a task scheduler that ensures a maximum concurrency level while
// running on top of the thread pool.
public class LimitedConcurrencyLevelTaskScheduler : TaskScheduler
{
// Indicates whether the current thread is processing work items.
[ThreadStatic]
private static bool _currentThreadIsProcessingItems;
// The list of tasks to be executed
private readonly LinkedList<Task> _tasks = new LinkedList<Task>(); // protected by lock(_tasks)
public int CountRunning => _nowRunning;
public int CountQueued
{
get
{
lock (_tasks)
{
return _tasks.Count;
}
}
}
// The maximum concurrency level allowed by this scheduler.
private readonly int _maxDegreeOfParallelism;
// Indicates whether the scheduler is currently processing work items.
private volatile int _delegatesQueuedOrRunning = 0;
private volatile int _nowRunning;
// Creates a new instance with the specified degree of parallelism.
public LimitedConcurrencyLevelTaskScheduler(int maxDegreeOfParallelism)
{
if (maxDegreeOfParallelism < 1)
throw new ArgumentOutOfRangeException("maxDegreeOfParallelism");
_maxDegreeOfParallelism = maxDegreeOfParallelism;
}
// Queues a task to the scheduler.
protected sealed override void QueueTask(Task task)
{
// Add the task to the list of tasks to be processed. If there aren't enough
// delegates currently queued or running to process tasks, schedule another.
lock (_tasks)
{
_tasks.AddLast(task);
if (_delegatesQueuedOrRunning < _maxDegreeOfParallelism)
{
Interlocked.Increment(ref _delegatesQueuedOrRunning);
NotifyThreadPoolOfPendingWork();
}
}
}
// Inform the ThreadPool that there's work to be executed for this scheduler.
private void NotifyThreadPoolOfPendingWork()
{
ThreadPool.UnsafeQueueUserWorkItem(_ =>
{
// Note that the current thread is now processing work items.
// This is necessary to enable inlining of tasks into this thread.
_currentThreadIsProcessingItems = true;
try
{
// Process all available items in the queue.
while (true)
{
Task item;
lock (_tasks)
{
// When there are no more items to be processed,
// note that we're done processing, and get out.
if (_tasks.Count == 0)
{
Interlocked.Decrement(ref _delegatesQueuedOrRunning);
break;
}
// Get the next item from the queue
item = _tasks.First.Value;
_tasks.RemoveFirst();
}
// Execute the task we pulled out of the queue
Interlocked.Increment(ref _nowRunning);
if (base.TryExecuteTask(item))
Interlocked.Decrement(ref _nowRunning);
}
}
// We're done processing items on the current thread
finally { _currentThreadIsProcessingItems = false; }
}, null);
}
// Attempts to execute the specified task on the current thread.
protected sealed override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
{
// If this thread isn't already processing a task, we don't support inlining
if (!_currentThreadIsProcessingItems) return false;
// If the task was previously queued, remove it from the queue
if (taskWasPreviouslyQueued)
// Try to run the task.
if (TryDequeue(task))
return base.TryExecuteTask(task);
else
return false;
else
return base.TryExecuteTask(task);
}
// Attempt to remove a previously scheduled task from the scheduler.
protected sealed override bool TryDequeue(Task task)
{
lock (_tasks) return _tasks.Remove(task);
}
// Gets the maximum concurrency level supported by this scheduler.
public sealed override int MaximumConcurrencyLevel { get { return _maxDegreeOfParallelism; } }
// Gets an enumerable of the tasks currently scheduled on this scheduler.
protected sealed override IEnumerable<Task> GetScheduledTasks()
{
bool lockTaken = false;
try
{
Monitor.TryEnter(_tasks, ref lockTaken);
if (lockTaken) return _tasks;
else throw new NotSupportedException();
}
finally
{
if (lockTaken) Monitor.Exit(_tasks);
}
}
}
}
I have a search task that makes a request to an API, within a portable class library, when the user enters text in the textbox, this works as expected but I have a concern over performance at scale. When we have a large userbase all making requests to this API on every key press I can foresee performance issues.
I have limited the API call to only fire when there are more than three valid characters but I want to dampen this further. I could implement a timer over the top of this but it does not feel like a good solution and is not present in the PCL framework.
Is there a recommended pattern to achieve this type of request dampening?
private async Task GetClubs()
{
try
{
if (!string.IsNullOrWhiteSpace(ClubSearch) && ClubSearch.Replace(" ", "").Length >= 3)
{
Clubs = await dataService.GetClubs(ClubSearch);
}
}
catch (DataServiceException ex)
{
...
}
}
Usually that is done with timer. When search text changes you start (or reuse) a timer which will fire after delay and execute search request. If more text is typed during that delay - timer is reset. Sample code:
public class MyClass {
private readonly Timer _timer;
const int ThrottlePeriod = 500; // ms
public MyClass() {
_timer = new System.Threading.Timer(_ => {
ExecuteRequest();
}, null, Timeout.Infinite, Timeout.Infinite);
}
private string _searchTerm;
public string SearchTerm
{
get { return _searchTerm; }
set
{
_searchTerm = value;
ResetTimer();
}
}
private void ResetTimer() {
_timer.Change(ThrottlePeriod, Timeout.Infinite);
}
private void ExecuteRequest() {
Console.WriteLine(SearchTerm);
}
}
If timer is not available, you can do the same with Task.Delay:
public class MyClass
{
const int ThrottlePeriod = 500; // ms
private string _searchTerm;
public string SearchTerm
{
get { return _searchTerm; }
set
{
_searchTerm = value;
SearchWithDelay();
}
}
private async void SearchWithDelay() {
var before = this.SearchTerm;
await Task.Delay(ThrottlePeriod);
if (before == this.SearchTerm) {
// did not change while we were waiting
ExecuteRequest();
}
}
private void ExecuteRequest()
{
Console.WriteLine(SearchTerm);
}
}
Cheap/Fast way to implement this is a Task.Delay:
var mySearchThread = new Thread (new ThreadStart (async delegate {
while (true) {
if (!String.IsNullOrWhiteSpace(seachText) {
YourSearchMethod(seachText)
};
InvokeOnMainThread ( () => {
// Refresh your datasource on the UIthread
});
await Task.Delay (2000);
}
})).Start ();
A PCL-based solution (and amazing clean way with a great framework) is to use ReactiveUI throttling (Throttle), then you can do feats like:
// Throttle searching to every 2 seconds
this.WhenAnyValue(x => x.SearchText)
.Where(x => !String.IsNullOrWhiteSpace(x))
.Throttle(TimeSpan.FromSeconds(2))
.InvokeCommand(SearchCommand)
Ref: http://reactiveui.net
Ref: http://docs.reactiveui.net/en/user-guide/when-any/index.html
I would like to write a method that will await for a variable to be set to true.
Here is the psudo code.
bool IsSomethingLoading = false
SomeData TheData;
public async Task<SomeData> GetTheData()
{
await IsSomethingLoading == true;
return TheData;
}
TheData will be set by a Prism Event along with the IsSomethingLoading variable.
I have a call to the GetTheData method, but I would like it to run async (right now it just returns null if the data is not ready. (That leads to other problems.)
Is there a way to do this?
In many situations like this what you need is a TaskCompletionSource.
You likely have a method that is able to generate the data at some point in time, but it doesn't use a task to do it. Perhaps there is a method that takes a callback which provides the result, or an event that is fired to indicate that there is a result, or simply code using a Thread or ThreadPool that you are not inclined to re-factor into using Task.Run.
public Task<SomeData> GetTheData()
{
TaskCompletionSource<SomeData> tcs = new TaskCompletionSource<SomeData>();
SomeObject worker = new SomeObject();
worker.WorkCompleted += result => tcs.SetResult(result);
worker.DoWork();
return tcs.Task;
}
While you may need/want to provide the TaskCompletionSource to the worker, or some other class, or in some other way expose it to a broader scope, I've found it's often not needed, even though it's a very powerful option when it's appropriate.
It's also possible that you can use Task.FromAsync to create a task based on an asynchronous operation and then either return that task directly, or await it in your code.
You could use a TaskCompletionSource as your signal, and await that:
TaskCompletionSource<bool> IsSomethingLoading = new TaskCompletionSource<bool>();
SomeData TheData;
public async Task<SomeData> GetTheData()
{
await IsSomethingLoading.Task;
return TheData;
}
And in your Prism event do:
IsSomethingLoading.SetResult(true);
This work for me:
while (IsLoading) await Task.Delay(100);
I propose a very simple solution but not the best to answer the original question, if you are not regarding at speed performance :
...
public volatile bool IsSomethingLoading = false;
...
public async Task<SomeData> GetTheData()
{
// Launch the task asynchronously without waiting the end
_ = Task.Factory.StartNew(() =>
{
// Get the data from elsewhere ...
});
// Wait the flag
await Task.Factory.StartNew(() =>
{
while (IsSomethingLoading)
{
Thread.Sleep(100);
}
});
return TheData;
}
Important note : #Theodor Zoulias proposed : IsSomethingLoading shall be declared with volatile keyword, to avoid compiler optimizations and potential multithread issues when accessing from other threads.
For further information about compilator omptimizations follow this article :
The C# Memory Model in Theory and Practice
I'm adding a full test code below :
XAML :
<Label x:Name="label1" Content="Label" HorizontalAlignment="Left" Margin="111,93,0,0" VerticalAlignment="Top" Grid.ColumnSpan="2" Height="48" Width="312"/>
Test Code :
public partial class MainWindow : Window
{
// volatile keyword shall be used to avoid compiler optimizations
// and potential multithread issues when accessing IsSomethingLoading
// from other threads.
private volatile bool IsSomethingLoading = false;
public MainWindow()
{
InitializeComponent();
_ = TestASyncTask();
}
private async Task<bool> TestASyncTask()
{
IsSomethingLoading = true;
label1.Content = "Doing background task";
// Launch the task asynchronously without waiting the end
_ = Task.Factory.StartNew(() =>
{
Thread.Sleep(2000);
IsSomethingLoading = false;
Thread.Sleep(5000);
HostController.Host.Invoke(new Action(() => label1.Content = "Background task terminated"));
});
label1.Content = "Waiting IsSomethingLoading ...";
// Wait the flag
await Task.Run(async () => { while (IsSomethingLoading) { await Task.Delay(100); }});
label1.Content = "Wait Finished";
return true;
}
}
/// <summary>
/// Main UI thread host controller dispatcher
/// </summary>
public static class HostController
{
/// <summary>
/// Main Host
/// </summary>
private static Dispatcher _host;
public static Dispatcher Host
{
get
{
if (_host == null)
{
if (Application.Current != null)
_host = Application.Current.Dispatcher;
else
_host = Dispatcher.CurrentDispatcher;
}
return _host;
}
}
}
There is this class unit that has a property bool status that marks whether a method, request, should be called on the unit. I have my other class, and in it, there is a method that should call request. To avoid blocking the main thread, I want to call the method asynchronously. The problem is that there isn't an event for the status change, and I don't want to make my asynchronous call do ugly stuff like:
while(!status){}unit.request(args);
or
while(!status){Thread.Sleep(100)}unit.request(args);
especially when I do not know the timescale in which status turns true.
How do I do this?
update: i forgot to mention that i cannot change unit. sorry for that.
You want to call a function (be it asynchronously or not) when a property changes. You have two choices:
Attach to an even that is signalled when the property changes
Periodically check the value of the property
You can't do the first, so you must do the second.
This is a sample of how you can manage this using an event.
Suppose this is your class
public class Unit
{
private readonly object _syncRoot = new object();
private bool _status;
public event EventHandler OnChanged;
public bool Status
{
get
{
lock (_syncRoot)
{
return _status;
}
}
set
{
lock (_syncRoot)
{
_status = value;
if (_status && OnChanged != null)
{
OnChanged.Invoke(this, null);
}
}
}
}
public void Process()
{
Thread.Sleep(1000);
Status = true;
}
}
Here is how you can use it
class Program
{
static void Main(string[] args)
{
var unit = new Unit();
unit.OnChanged += Unit_OnChanged;
Console.WriteLine("Before");
Task.Factory.StartNew(unit.Process);
Console.WriteLine("After");
Console.WriteLine("Manual blocking, or else app dies");
Console.ReadLine();
}
static void Unit_OnChanged(object sender, EventArgs e)
{
//Do your processing here
Console.WriteLine("Unit_OnChanged before");
Task.Factory.StartNew(()=>
{
Thread.Sleep(1000);
Console.WriteLine("Unit_OnChanged finished");
});
Console.WriteLine("Unit_OnChanged after");
}
}
This outputs
Before
After
Manual blocking, or else app dies
Unit_OnChanged before
Unit_OnChanged after
Unit_OnChanged finished
This is the classic polling problem, and there really isn't an elegant solution when polling is concerned. But we can work some functional programming in to get something which isn't a nightmare to use.
public static CancellationTokenSource Poll(
Func<bool> termination,
Action<CancellationToken> onexit,
int waitTime = 0,
int pollInterval = 1000)
{
var cts = new CancellationTokenSource();
var token = cts.Token;
Action dispose = cts.Cancel;
var timer = new Timer(_ =>
{
if (termination() || token.IsCancellationRequested)
{
onexit(token);
dispose();
}
}, null, waitTime, pollInterval);
dispose = timer.Dispose;
return cts;
}
Example:
var condition = false;
Poll(() => condition == true, ct => Console.WriteLine("Done!"));
Console.ReadLine();
condition = true;
Console.ReadLine();
Use a System.Threading.AutoResetEvent instead of a bool if possible:
AutoResetEvent status = new AutoResetEvent();
In your asynchronous method, wait for it:
status.WaitOne();
unit.request(args);
Then, to signal it in your other class, call Set:
status.Set();