I want to use a background task for my UWP app.
The below code, is my back button click event in windows mobile:
private async void MainPage_BackRequested(object sender, BackRequestedEventArgs e)
{
var access= await BackgroundExecutionManager.RequestAccessAsync();
var task = new BackgroundTaskBuilder
{
Name="My task",TaskEntryPoint=typeof(backGroundTask.Class1).ToString()
};
trigger = new ApplicationTrigger();
task.SetTrigger(trigger);
task.Register();
//var result = await trigger.RequestAsync();
if (Frame.CanGoBack)
{
Frame.GoBack();
e.Handled = true;
}
}
public void Run(IBackgroundTaskInstance taskInstance)
{
_deferral = taskInstance.GetDeferral();
clearData();
count1 = 0;
getDownloadedSongs();
dispatcherTimer1.Tick += DispatcherTimer1_Tick;
dispatcherTimer1.Interval = new TimeSpan(0, 0, 3);
dispatcherTimer1.Start();
_deferral.Complete();
}
DispatcherTimer dispatcherTimer1 = new DispatcherTimer();
private async void DispatcherTimer1_Tick(object sender, object e)
{
try
{
clearData();
}
catch (Exception ex)
{
}
}
But this code is not working, when I click back button. As per expectation background task code should work, but it is not working. What am I doing wrong?
Your background task is exiting before the DispatcherTimer gets a chance to ever execute, because you mark the Deferral as complete. You should hold on to the Deferral until all the work in your background task has been completed (or until you receive a TaskCanceled event from the system).
Related
So I'm basically trying to delay the invocation of filter process by 1.5 seconds to allow user to type multiple keystrokes in case they want to. If a new keystroke is typed, previously waiting task is cancelled and a new one starts waiting:
System.Threading.CancellationTokenSource token = new System.Threading.CancellationTokenSource();
private async void MyTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
token.Cancel();
await System.Threading.Tasks.Task.Delay(1500, token.Token);
this.filterText = (sender as TextBox).Text;
(this.Resources["CVS"] as CollectionViewSource).View.Refresh();
//Earlier I had tried this variant too:
//System.Threading.Tasks.Task.Delay(500, token.Token).ContinueWith(_ =>
//{
// this.filterText = (sender as TextBox).Text;
// (this.Resources["CVS"] as CollectionViewSource).View.Refresh();
//});
}
But the filter process (View.Refresh() line) hits immediately on first keystroke without waiting. My impression was that calling Cancel on the token would kill Delay() and thereby the continuation task too, before planting the next one, but apparently this scheme doesn't work.
What am I missing?
The proper way to handle this is not with Task.Delay and exceptions (as exceptions are for exceptional circumstances), but using a Timer with the Timer.Elapsed event.
E.g.
using Timer = System.Windows.Forms.Timer;
private readonly Timer timer = new Timer();
private static string newText = "";
public Form1()
{
timer.Interval = 1500;
timer.Tick += OnTimedEvent;
}
private void MyTextBox_TextChanged(object sender, EventArgs e)
{
timer.Stop(); // sets the time back to 0
newText = (sender as TextBox).Text; // sets new text
timer.Start(); // restarts the timer
}
private void OnTimedEvent(Object source, EventArgs e)
{
filterText = newText;
(Resources["CVS"] as CollectionViewSource).View.Refresh();
}
(Not sure this is 100% correct, but you get the gist.)
Old snippet relating to the discussions in the comments.
As the post says: this is not needed, as Task.Delay will link a listener to the CancellationToken, thus .Cancel() will block until all listeners have heard it.
using System.Threading;
using System.Threading.Tasks;
private CancellationTokenSource cts = new CancellationTokenSource();
private Task delayTask;
private async void TenantsFilter_TextChanged(object sender, TextChangedEventArgs e)
{
cts.Cancel();
if (delayTask != null) {
try{await delayTask;}
catch(TaskCanceledException){}
}
cts = new CancellationTokenSource();
try
{
delayTask = Task.Delay(1500, cts.Token);
await delayTask;
this.filterText = (sender as TextBox).Text;
(this.Resources["CVS"] as CollectionViewSource).View.Refresh();
}
catch(TaskCanceledException)
{
}
}
If this helps anyone, the following is correctly working for me. My mistake was that I incorrectly assumed that CancellationTokenSource is a signaling device and could be used multiple times. That is apparently not the case:
private System.Threading.CancellationTokenSource cts = new System.Threading.CancellationTokenSource();
private async void TenantsFilter_TextChanged(object sender, TextChangedEventArgs e)
{
cts.Cancel();
cts = new System.Threading.CancellationTokenSource();
try
{
await System.Threading.Tasks.Task.Delay(1500, cts.Token);
this.filterText = (sender as TextBox).Text;
(this.Resources["CVS"] as CollectionViewSource).View.Refresh();
}
catch(System.Threading.Tasks.TaskCanceledException ee)
{
}
}
Posting it here for my own record and just to let others check I'm still not doing anything wrong.
I am using an MVVM model in my WPF application. I have an command binding to the cancel button. I have a start button which starts a few background workers. When i click on the cancel button, i want all the background workers to stop/quit.
With my current code when i click on cancel button, the background worker does not stop and the "StartEngineeringOperation" finishes. Can anyone please help me out with what i am doing wrong here?
Current code:
For EngineeringViewModel.cs:
public class EngineeringViewModel{
public EngineeringViewModel()
{
StartEngineering= new DelegateCommand(o =>
{
worker = new BackgroundWorker
{
WorkerReportsProgress = true,
WorkerSupportsCancellation = true
};
worker.ProgressChanged += Worker_ProgressChanged;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
if (worker.IsBusy != true) worker.RunWorkerAsync();
worker.DoWork += (s, e) =>
{
StartEngineeringOperation();
if (worker.CancellationPending)
{
e.Cancel = true;
return;
}
};
},
k => true);
Cancel = new DelegateCommand(CancelEngineeringOperation);
}
private void StartEngineeringOperation()
{
startAlarmService();
startTrendQualityCheck();
}
private void CancelEngineeringOperation(object param)
{
worker.DoWork += (s, e) =>
{
if (worker.IsBusy)
{
worker.CancelAsync();
e.Cancel = true;
return;
}
};
}
}
I tried this :
but doesn't seem to work:
private void StartEngineeringOperation()
{
startAlarmService();
if (worker.CancellationPending)
{
e.Cancel = true;
return;
}
startTrendQualityCheck();
}
As you may have learned from te comments, you need to poll the state of the BackgroundWorker in your operations that you want to support cancellation. Then take measures to cancel the ongoing operation gracefully.
The example shows how to cancel a background thread on button click. The first example uses the old BackgroundWorker and the second the modern and cleaner Task library.
BackgroundWorker
private BackgroundWorker Worker { get; set; }
private void StartWorker()
{
this.Worker = new BackgroundWorker
{
WorkerReportsProgress = true,
WorkerSupportsCancellation = true
};
this.Worker.DoWork += BackgroundWorker_DoWork;
}
private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
DoCancellableWork();
// Stop BackgroundWorker from executing
if (worker.CancellationPending)
{
e.Cancel = true;
}
}
private void DoCancellableWork()
{
// Check for cancellation before executing the cancellable operation and allocating resources etc..
if (this.Worker.CancellationPending)
{
return;
}
// Periodically/regularly check for the cancellation flag
for (int i = 0; i <= 10000000000; i++)
{
if (this.Worker.CancellationPending)
{
// Cancel operation gracefully e.g., do some cleanup, free resources etc.
return;
}
// Do some work
}
}
// Alternatively use a command e.g., in a view model class
private void CancelBackgroundWorker_Click(object sender, EventArgs e)
{
if (this.Worker.WorkerSupportsCancellation)
{
this.Worker.CancelAsync();
}
}
Task library
The example uses Progress<T> to report progress from the background thread to the UI thread.
private CancellationTokenSource CancellationTokenSource { get; set; }
private async Task StartWorker()
{
this.CancellationTokenSource = new CancellationTokenSource();
// Prepare callback to update UI from the background thread.
// The Progress<T> instance MUST be created on the UI thread
IProgress<int> progressReporter = new Progress<int>(progress => this.ProgressBar.Value = progress);
await Task.Run(
() => DoWork(progressReporter, this.CancellationTokenSource.Token),
this.CancellationTokenSource.Token);
this.CancellationTokenSource.Dispose();
}
private void DoWork(IProgress<int> progressReporter, CancellationToken cancellationToken)
{
DoCancellableWork(progressReporter, cancellationToken);
}
private void DoCancellableWork(IProgress<int> progressReporter, CancellationToken cancellationToken)
{
// Check for cancellation before executing the operation and allocating resources etc..
if (cancellationToken.IsCancellationRequested)
{
return;
}
// Periodically/regularly check for the cancellation flag
for (int i = 0; i <= 10000000000; i++)
{
if (cancellationToken.IsCancellationRequested)
{
// Cancel operation gracefully e.g., do some cleanup, free resources etc.
return;
}
// Do some work
// Report progress
progressReporter.Report(20);
}
}
// Alternatively use a command e.g., in a view model class
private void CancelBackgroundThread_Click(object sender, EventArgs e)
{
this.CancellationtokenSource?.Cancel();
}
Since the OP describes the task being done as "checking services", I would assume the work done looks something like this:
while(true){
// check service
// Post result back to UI thread
Thread.Sleep(...);
}
This is not the best way to write such such a check. As in most cases where Thread.Sleep is used, a timer would be a better alternative:
var myTimer = new System.Timers.Timer(...);
myTimer .Elapsed += OnTimedEvent;
myTimer .AutoReset = true;
myTimer .Enabled = true;
...
private void OnTimedEvent(Object source, ElapsedEventArgs e)
{
// check service
// Post result back to UI thread
}
This makes the problem of stopping/starting the task being done a simple matter of changing the Enabled-flag of the timer. It is also possible to use a timer, or a synchronization context to run the event directly on the UI thread, this is probably the best solution if "checking services" only takes a few ms.
I am using a loop each 7 seconds. This loop communicates with a server. Then when server is answering i am waiting user to answer about servers response. So i need to check if all this process has finished.
So i am creating a timer
var backgroundWorker = new BackgroundWorker();
backgroundWorker.DoWork += (sender, e) => backgroundWorker_DoWork(sender, e, frm);
var timer = new System.Timers.Timer();
timer.Enabled = true;
timer.Interval = 10000;
timer.Elapsed += (sender, e) => Timer_Elapsed(sender, e);
timer.Start();
private static void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
if (!backgroundWorker.IsBusy)
{
backgroundWorker.RunWorkerAsync();
}
}
private static async void backgroundWorker_DoWork(object sender, DoWorkEventArgs e, Main frm)
{
using (var client = new HttpClient())
{
var result = await client.GetAsync(uri);
//message to user for answering, then return it to server again
}
}
For some reason when i am using await..., 'backgroundWorker.IsBusy' flag turns to false. I mean that i can't have correct result if task has actually finished. Should i use a flag for this? or there is more efficient way?
You can't use await inside a BackgroundWorker's DoWork. I recommend replacing BackgroundWoker completely with Task.Run:
Task task = null;
var timer = new Timer();
timer.Interval = 10000;
timer.Tick += Timer_Tick;
timer.Start();
private static async void Timer_Tick(object sender, EventArgs e)
{
if (task == null)
{
task = Task.Run(() => DoWorkAsync(frm));
try { await task; }
finally { task = null; }
}
}
private static async Task DoWorkAsync(Main frm)
{
using (var client = new HttpClient())
{
var result = await client.GetAsync(uri);
//message to user for answering, then return it to server again
}
}
Side note: I changed System.Timers.Timer to System.Windows.Forms.Timer so that the task variable is always accessed from the UI thread.
When i'm using async task result inside bool button application is stuck
async Task<bool> task1()
{
await Task.Run(() =>
{
for (int a = 0; a <= 1000000000; a++)
{
}
});
return true;
}
private void Start_Click(object sender, EventArgs e)
{
setDialog(true);
if (task1().Result==true)
setDialog(false);
}
private void setDialog(bool show)
{
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.SetView(Resource.Layout.layout1);
Dialog dialog = builder.Create();
dialog.SetCancelable(false);
if (show) dialog.Show();
else dialog.Dismiss();
}
When i'm pressing start button click my application is stuck and doesnt let me do anything.
If i will use task inside button event then everything is going fine
You deadlocked your application. This: task1().Result waits synchronously and defeats the purpose of tasks. Use async/await like this instead:
private async void Start_Click(object sender, EventArgs e)
{
setDialog(true);
var result = await task1();
if (result)
setDialog(false);
}
I have developed the below code using TPL and all works fine except how to restart the task. Currently when I click button1 the thread starts in textBox1 and similarly with button3 and textBox2. When I click button3 and button4 both the threads get's cancelled.
Now I want to restart the thread where left off on the click event of button1 and button3. I tried many approaches but could not get any idea on how to do this.
Here's the code:
public partial class Form1 : Form
{
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
CancellationToken token;
CancellationTokenSource cancellationTokenSource1 = new CancellationTokenSource();
CancellationToken token1;
public Form1()
{
InitializeComponent();
token = cancellationTokenSource.Token;
token1 = cancellationTokenSource1.Token;
}
private void button1_Click(object sender, EventArgs e)
{
Task t = Task.Factory.StartNew(() =>
{
var run = true;
while (run)
{
for (int i = 0; i < 100; i++)
{
if (token.IsCancellationRequested)
{
run = false;
break;
//token.ThrowIfCancellationRequested();
}
// your code
System.Threading.Thread.Sleep(1000);
Action act = () => textBox1.Text = Convert.ToString(i);
textBox1.Invoke(act);
}
}
});
}
private void button2_Click(object sender, EventArgs e)
{
cancellationTokenSource.Cancel();
}
private void button3_Click(object sender, EventArgs e)
{
Task t1 = Task.Factory.StartNew(() =>
{
var run = true;
while (run)
{
for (int i = 0; i < 100; i++)
{
if (token1.IsCancellationRequested)
{
run = false;
break;
//token1.ThrowIfCancellationRequested();
}
// your code
System.Threading.Thread.Sleep(1000);
Action act = () => textBox2.Text = Convert.ToString(i);
textBox2.Invoke(act);
}
}
});
}
private void button4_Click(object sender, EventArgs e)
{
cancellationTokenSource1.Cancel();
}
}
You need to sync thread using signaling, generally they are achieved using Event Wait Handles like AutoResetEvent, ManualReset and CountdownEvent. You can achieve this with ManualReset.
Declare ManualResetEvent:
CancellationTokenSource cancellationTokenSource1 = new CancellationTokenSource();
CancellationToken token1;
private ManualResetEvent mre = new ManualResetEvent(false);
Modify on CancellationRequest
if (token.IsCancellationRequested) {
run = false;
mre.WaitOne();
//token.ThrowIfCancellationRequested();
}
OnStart/OnResume: mre.Set();