I am trying to make a background task reschedule itself at the end of it's task. but when I go to test it doesn't seem to activate. The task runs initially from the frontend successfully. When I check the lifecycle Events after it finishes I see it's name and two blank ones. When I run the blank ones it runs it, not sure what I am doing that causes them. I am tryin to test with a 16 min time trigger but it doesn't seem to ever run again. This is the code:
var SleepyBand_TaskName = "DataHandlerTask";
foreach (var task in BackgroundTaskRegistration.AllTasks)
{
if (task.Value.Name == SleepyBand_TaskName)
{
task.Value.Unregister(true);
}
}
var builder = new BackgroundTaskBuilder();
var trigger = new TimeTrigger(16, false);
builder.Name = SleepyBand_TaskName;
builder.TaskEntryPoint = "SleepyBand_BackgroundTasks.DataHandlerTask";
builder.SetTrigger(trigger);
builder.Register();
TimeTrigger only work for lock-screen apps.
On Windows, a background task will only run using a TimeTrigger if you have requested that your app be placed on the lock screen with a call to RequestAccessAsync and the user accepts the prompt
You would need to use a MaintenanceTrigger
eI would just use System.Threading.Tasks.Task to start threads. Like this you can reshedule a task:
public void Test() {
Task.Run(() => DoSomething());
}
private void DoSomething() {
//Do Something here....
//Do Something again...
Task.Run(() => DoSomething());
}
If you need delays use something like this: https://actionscheduler.codeplex.com/
Related
I am using a UWP project with a background task that is triggered by the internet being available. Once triggered, a toast notification is displayed.
The problem is that the background task seems to only run once after launching the UWP application. it even works after closing the application and restarting my computer as long as I haven't triggered it before doing so, but only if it is untriggered before restarting.
What am I doing wrong? am I missing something or misusing the background task?
For clarification, I want it to send a notification every time the internet is connected. The background task should run independent of the main application.
Below is the code for the background task:
namespace AppService
{
public sealed class testNoteUpdaterTask : IBackgroundTask
{
BackgroundTaskDeferral _deferral; // Note: defined at class scope so that we can mark it complete inside the OnCancel() callback if we choose to support cancellation
public void Run(IBackgroundTaskInstance taskInstance)
{
// Get a deferral so that the service isn't terminated.
_deferral = taskInstance.GetDeferral();
// Construct the content
new ToastContentBuilder()
.AddArgument("action", "testNote")
.AddArgument("conversationId", 9813)
.AddText("Program update avaliable for testNote")
// Buttons
.AddButton(new ToastButton()
.SetContent("testNote stuff")
.AddArgument("action", "open")
.SetBackgroundActivation())
.Show();
_deferral.Complete();
}
}
}
And here is the code which I use to register the background task inside the main UWP application:
public static BackgroundTaskRegistration RegisterBackgroundTask(string taskEntryPoint, string taskName, IBackgroundTrigger trigger, IBackgroundCondition condition = null)
{
// Check for existing registrations of this background task.
foreach (var cur in BackgroundTaskRegistration.AllTasks) {
if (cur.Value.Name == taskName){
// The task is already registered.
return (BackgroundTaskRegistration)(cur.Value);
}
}
// Register the background task.
var builder = new BackgroundTaskBuilder();
builder.Name = taskName;
builder.TaskEntryPoint = taskEntryPoint;
builder.SetTrigger(trigger);
if (condition != null) {
builder.AddCondition(condition);
}
BackgroundTaskRegistration task = builder.Register();
return task;
}
public MainPage()
{
this.InitializeComponent();
RegisterBackgroundTask("AppService.testNoteUpdaterTask", "testNoteUpdaterX", new SystemTrigger(SystemTriggerType.InternetAvailable, true));
}
I checked your code. It seems that when you are registering the SystemTrigger here:
new SystemTrigger(SystemTriggerType.InternetAvailable, true)
You are setting the oneShot parameter as true, which means the system event trigger will be used only once. Please set this value to false if you want the system event trigger to be used every time the event occurs.
More information here:SystemTrigger(SystemTriggerType, Boolean) Constructor.
Please use the following code:
new SystemTrigger(SystemTriggerType.InternetAvailable, false)
You could also take a look at the official background task sample here:
BackgroundTask Sample line 166.
[EDIT]Solved, see below[/EDIT]
this is a newbie-question.
I'm just digging in to c# and async and whyt i would like to have:
click Button
run several tasks in order but in background-thread, one after another
running tasks should notifiy their progress if possible
right now i can click the botton and start the task-chain, but within the completition event i would like (for testing) show a message-box every time a task has finished. this may lead to a crash (?) and i don't know why since i thought i would be within the ui-thread ...
here are some parts of the code:
AppViewModel:
void handlePhaseCompletedEvent(object sender, SyncPhaseCompletedEventArgs e)
{
Shell.Current.DisplayAlert("TEST", "PHASE " + e.phase.ToString(), "OK"); // <<<< doesn't show up, maybe because its crashing a short time after?
syncToolService.StartSyncPhaseAsync(e.phase + 1, this); // <<<< seems to crash here?
}
[RelayCommand]
async Task StartSyncAsync()
{
syncToolService.NotifySyncPhaseCompleted += handlePhaseCompletedEvent;
syncToolService.StartSyncPhaseAsync(0, this);
}
syncToolService:
public event EventHandler<SyncPhaseCompletedEventArgs> NotifySyncPhaseCompleted;
public async Task StartSyncPhaseAsync(int phase, AppViewModel viewModel)
{
// search for Remote-peer
if (phase == 0)
{
Task t = new Task(() => Task.Delay(100)); // dummy, not implemented yet
t.ConfigureAwait(false);
t.ContinueWith(t => NotifySyncPhaseCompleted?.Invoke(this, new SyncPhaseCompletedEventArgs { phase = phase }));
t.Start();
return;
}
// Remote Sync start preparations
if (phase == 1)
{
Task t = new Task(() => Task.Delay(100)); // dummy, not implemented yet
t.ConfigureAwait(false);
t.ContinueWith(t => NotifySyncPhaseCompleted?.Invoke(this, new SyncPhaseCompletedEventArgs { phase = phase }));
t.Start();
return;
}
//////// LOCAL PREPARATIONS
// read local files
if (phase == 2)
{
Task t = new Task(() => BPMSyncToolService.loadLocalData(viewModel.DataFiles));
t.ConfigureAwait(false);
t.ContinueWith(t => NotifySyncPhaseCompleted?.Invoke(this, new SyncPhaseCompletedEventArgs { phase = phase }));
t.Start();
return;
}
}
basicly i thought StartSyncPhaseAsync would run a Task (and it seems to do so)
and it also seems to trigger the event (whicht seems not to raise the exeption)
when running line by line in debug it crashes after syncToolService.StartSyncPhaseAsync(e.phase + 1, this);
with this stack:
> [Exception] WinRT.Runtime.dll!WinRT.ExceptionHelpers.ThrowExceptionForHR.__Throw|20_0(int hr)
[Exception] Microsoft.WinUI.dll!Microsoft.UI.Xaml.Controls.ContentDialog._IContentDialogFactory.CreateInstance(object baseInterface, out System.IntPtr innerInterface)
[Exception] Microsoft.WinUI.dll!Microsoft.UI.Xaml.Controls.ContentDialog.ContentDialog()
[Exception] Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.Platform.AlertManager.AlertRequestHelper.OnAlertRequested(Microsoft.Maui.Controls.Page sender, Microsoft.Maui.Controls.Internals.AlertArguments arguments)
System.Private.CoreLib.dll!System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Private.CoreLib.dll!System.Threading.Tasks.Task.ThrowAsync.AnonymousMethod__128_1(object state)
System.Private.CoreLib.dll!System.Threading.QueueUserWorkItemCallbackDefaultContext.Execute()
System.Private.CoreLib.dll!System.Threading.ThreadPoolWorkQueue.Dispatch()
System.Private.CoreLib.dll!System.Threading.PortableThreadPool.WorkerThread.WorkerThreadStart()
i also may have a general problem in my design, any help would be great!
[UPDATE]
it runs now as expected.
newbie-thoughts:
the answer from ToolmakerSteve https://stackoverflow.com/a/73409415/4232410 i thought "hey, but thats what i tried first and it locked UI". Then i've watched https://www.youtube.com/watch?v=2moh18sh5p4&t=0s and https://www.youtube.com/watch?v=ZTKGRJy5P2M and i saw "hey, its basicly what was mentioned and there it works, so where is my fault (now as i'm writing this i saw his update, thanks alot!)
Ryan mentioned "ReportProgress" (that was the way i stumbled across the above videos), and it worked, also thank you!
so this is basicly the actual working code that seems NOT to lock the UI and doesn't crash (the crash was because of Microsoft.VisualBasic.FileIO.TextFieldParser that tried to read a line and found a field beginning with a quote and thought it would be an enclosing quote which it wasn't)
AppViewModel:
private void HandleSyncProgressChanged(object sender, SyncStatus e)
{
NumFilesProcessed = e.filesProcessed;
NumFilesNotFound = e.filesNotFound;
AktueleAufgabe = e.workingPhase;
}
[RelayCommand]
async Task StartSyncAsync()
{
Progress<SyncStatus> progress=new Progress<SyncStatus>();
progress.ProgressChanged += HandleSyncProgressChanged;
await BPMSyncToolService.StartSyncPhaseAsync(this, progress);
}
syncToolService:
public static async Task StartSyncPhaseAsync(AppViewModel viewModel, IProgress<SyncStatus> progress)
{
SyncStatus report = new SyncStatus();
report.workingPhase = "Suche Synchronisationspartner";
progress.Report(report);
// search for Remote-peer
await Task.Delay(100); // dummy, not implemented yet
report.workingPhase = "Starte Vorbereitungen beim Synchronisationspartner";
progress.Report(report);
// Remote Sync start preparations
await Task.Delay(100); // dummy, not implemented yet
//////// LOCAL PREPARATIONS
report.workingPhase = "lese lokale Dateien";
progress.Report(report);
// read local files
await BPMSyncToolService.LoadLocalDataAsync(viewModel.DataFiles, progress, report);
// [...]
}
what i actually can't see is the counting up of processed files, maybe it's too fast, don't know, will see in further tasks that will require more time
anyways, thanks, both answers helped, i will mark the one as solution, that was closer to the core problem (i think)
Given async/await, it is almost never necessary to use task continuations or ConfigureAwait.
To start a sequence in the background, wrap the sequence in Task.Run.
To report progress on UI thread, use Dispatcher.Dispatch.
Example:
// IMPORTANT: `await`.
// Otherwise, current method would continue before Task.Run completes.
await Task.Run(async () =>
{
// Now on background thread.
...
// Report progress to UI.
Dispatcher.Dispatch(() =>
{
// Code here is queued to run on MainThread.
// Assuming you don't need to wait for the result,
// don't need await/async here.
}
// Still on background thread.
...
};
// This is effectively the "continuation": Code here runs after Task.Run completes.
...
UPDATE
In response to comment, this is how you use async/await to start a sequence of tasks, without waiting for the result:
If your top-level code does UI calls:
// This queues an independent execution to MainThread.
// We don't "await" the Dispatch, because we want it to run independently.
Dispatcher.Dispatch(async () => await TopMethod());
If your top-level code does not do UI calls:
// This queues an independent execution to the Thread Pool.
// We don't "await" the Run, because we want it to run independently.
Task.Run(async () => await TopMethod());
In either case, instead of using continuations, TopMethod uses awaits to sequence the tasks:
async void TopMethod()
{
await ..Task1..;
await ..Task2..;
await ..Task3..;
}
This is equivalent to Task1.ContinueWith(Task2.ContinueWith(Task3));
(Off the top of my head; I may not have the syntax quite right on this.)
If you are on a background thread (did Task.Run), then to do UI calls, simply wrap in Dispatcher.Dispatch( ... ). As shown in first code snippet.
You can capture SynchronizationContext in your syncToolService in constructor, or by defining explicitly API for capturing, kinda:
public void CaptureSynchronizationContext(SynchronizationContext context)
{
var current = SynchronizationContext.Current;
if (context is null)
{
this.capturedScheduler = TaskScheduler.Current;
return;
}
SynchronizationContext.SetSynchronizationContext(context);
this.capturedScheduler = TaskScheduler.FromCurrentSynchronizationContext();
SynchronizationContext.SetSynchronizationContext(current);
}
Add make some wrapper for your logic to be called in specified context:
private void RunTaskWithContinuation(Task task, Action<Task> continuation)
{
task.ConfigureAwait(false);
task.ContinueWith(t => continuation(t), capturedScheduler);
task.Start();
}
So, somewhere in your UI:
// afaik you should call it once per every Window
syncToolService.CaptureSynchronizationContext(SynchronizationContext.Current);
And your code above would look like this:
// read local files
if (phase == 2)
{
Task t = new Task(() => BPMSyncToolService.loadLocalData(viewModel.DataFiles));
RunTaskWithContinuation(t, () => NotifySyncPhaseCompleted?.Invoke(this, new SyncPhaseCompletedEventArgs { phase = phase }));
}
Not tested, but i would try this idea first.
Btw, if SynchronizationContext is null, guess your problem would be persisted.
There is space for refactoring, just wanted to show the idea.
UPDATE
There is ReportProgress type - right tool for reports in multithreaded environment. May be this is what you are looking for.
But it works the same way, as i did above - via context capturing.
Thread ThreadWindow = new Thread(async () =>
{
WindowWPF windowWPF = new WindowWPF();
windowWPF.Show();
await Task.Run(() =>
{
while (true)
{
//code
}
});
//code works when it shouldn't be available
});
For some reason, the compiler suggests changing the last line to me: }); in }){}; what is this for?
The idea is to display the loading window and work with the data in parallel, after which the window should close.
It will not, as soon as await is hit, an incomplete Task will be returned and the job within await will be returned as a future callback if it's a bit long running job.
As you have made while(true) making the piece of code to execute infinitely, assuming that you are trying to do call and forget way, If so then don't use await. Also, Instead of you creating new Thread, try making use of Task as below
var task = new Task(() => MyLongRunningMethod(),
TaskCreationOptions.LongRunning | TaskCreationOptions.PreferFairness);
task.Start();
I need to send a request to multiple servers and am trying to use tasks to run each connection asynchronously. I have a function that is configured to make the connections:
internal static Task<EventRecordEx> GetEventRecordFromServer(string server, string activityID)
I have tried the following but it runs synchronously...
var taskList = new List<Task<EventRecordEx>>();
foreach (string server in server_list)
{
taskList.Add(GetEventRecordFromServer(server, id));
}
await Task.Factory.ContinueWhenAll(taskList.ToArray(), completedTasks =>
{
foreach (var task in completedTasks)
{
// do something with the results
}
});
What am I doing wrong?
In my understanding when you use .ContinueWhenAll, you'll have a hard time debugging for the exceptions when one of the tasks fail as it will return an AggregateException, I'd suggest running the task individually, then use .ConfigureAwait(false) to make sure that it runs in a nun UI thread like so:
foreach(Task task in taskList.ToArray()){
await task.ConfigureAwait(false);
// Do something.
}
I am trying to run 3 of tasks on different threads (there will be a few more added.) The tasks that are called then call other tasks that are async / await.
The program execution continues after my command to wait. Execution needs to wait until all tasks are complete. My code is below (the null return is just there to test, I still need to create the return code.
public List<string> CopyFilesAsync(List<ModelIterationModel> model)
{
var copyFileTaskParameters = GetCopyFileTaskParameters(model);
Task<List<CopyFitDataResult>> fitDataResulLits = null;
Task<List<CopyNMStoreResult>> nmStoreResultsList = null;
Task<List<CopyDecompAnalyzerResult>> decompAnalyzerStoreResultsList = null;
Task parent = Task.Factory.StartNew(() =>
{
var cancellationToken = new CancellationToken();
TaskFactory factory = new TaskFactory(TaskCreationOptions.AttachedToParent, TaskContinuationOptions.ExecuteSynchronously);
factory.StartNew(() => fitDataResulLits = CopyFitDataFiles(copyFileTaskParameters, cancellationToken));
factory.StartNew(() => decompAnalyzerStoreResultsList = CopyDecompAnalyzerFiles(copyFileTaskParameters, cancellationToken));
factory.StartNew(() => nmStoreResultsList = CopyNMStoreResultsFiles(copyFileTaskParameters, cancellationToken));
});
parent.Wait();
return null;
}
The calling code is synchronous. Execution continues in this method before the tasks above complete.
public void CreateConfigFile(CreateConfigFileParameter parameter)
{
try
{
//data for this will need to come from UI, return values will include local file paths. All copy operations will be ran on it's own thread
//return value will include local file paths
var userFileListModel = _copyFilesToLocalDirectoryService.CopyFilesAsync(temp);
//will return object graph of data read from speadsheets and excel files
_readLocalFilesToDataModelService.ReadAllFiles();
//will take object graph and do date period logic and event type compression and any other business
//logic to extract an object model to create the config file
_processDataModelsToCreateConfigService.Process();
//will take extracted object model and use config file template to create the config file workbook
_writeConfigFileService.WriteConfigFile();
}
catch(Exception ex)
{
}
}
This code is in a class library in a WPF application. I don't know if that is important, but this is the first time I have had to interact with WPF (15 years of web development only.)
What do I need to do to stop execution until all tasks have completed? I played around with a few other approaches, such as attaching as children but nothing I do seems to work.
Edit - I keep trying approaches straight out of MSDN samples with no luck whatsoever. Just tried this
var cancellationToken = new CancellationToken();
var tasks = new List<Task>();
tasks.Add(Task.Run(() =>
{
fitDataResulLits = CopyFitDataFiles(copyFileTaskParameters, cancellationToken);
}));
Task t = Task.WhenAll(tasks.ToArray());
t.Wait();
Exactly like the MSDN sample, and I tried WaitAll but it runs right past it.
Could this have something to do with the Visual Studio debugger?
There are many questions to your code:
If you do not wait for files to be copied, how next lines of code should run?
Why do you need to create a TaskFactory to start a background work, which is already a Task?
Why do you create a CancellationToken? You need to create a CancellationTokenSource, and use it's Token for all your code you may need to cancel.
Also, this code:
tasks.Add(Task.Run(() =>
{
fitDataResulLits = CopyFitDataFiles(copyFileTaskParameters, cancellationToken);
}));
doesn't fire the CopyFitDataFiles, it simply assigns a task reference. You need to do this:
tasks.Add(CopyFitDataFiles(copyFileTaskParameters, cancellationToken));
Your code should be rewritten in this way:
public async Task<List<string>> CopyFilesAsync(List<ModelIterationModel> model)
{
var copyFileTaskParameters = GetCopyFileTaskParameters(model);
// do not await tasks here, just get the reference for them
var fitDataResulLits = CopyFitDataFiles(copyFileTaskParameters, cancellationToken);
// ...
// wait for all running tasks
await Task.WhenAll(copyFileTaskParameters, ...);
// now all of them are finished
}
// note sugnature change
public async Task CreateConfigFile
{
// if you really need to wait for this task after some moment, save the reference for task
var userFileListModel = _copyFilesToLocalDirectoryService.CopyFilesAsync(temp);
...
// now await it
await userFileListModel;
...
}
There is a great article about async/await: Async/Await - Best Practices in Asynchronous Programming by #StephenCleary