How to Execute a Method Every Second on a Background Thread So It Doesn't Affect the Performance of the App

How to execute a method every second on a background thread so it doesn't affect the performance of the app

Use Grand Central Dispatch :

DispatchQueue.global(qos: .background).async {
getDatabaseInfo()
}

Can code running in a background thread be faster than in the main VCL thread in Delphi?

Without simple source code to reproduce the issue, and how you are timing your threads, it will be difficult to understand what occurs in your software.

Sounds definitively like either:

  • An Architecture issue - how are your threads defined?
  • A measurement issue - how are you timing your threads?
  • A typical scaling issue of both the memory manager and the RTL string-related implementation.

About the latest point, consider this:

  • The current memory manager (FastMM4) is not scaling well on multi-core CPU; try with a per-thread memory manager, like our experimental SynScaleMM - note e.g. that the Free Pascal Compiler team has written a new scaling MM from scratch recently, to avoid such issue;
  • Try changing the string process implementation to avoid memory allocation (use static buffers), and string reference-counting (every string reference counting access produces a LOCK DEC/INC which do not scale so well on multi-code CPU - use per-thread char-level process, using e.g. PChar on static buffers instead of string).

I'm sure that without string operations, you'll find that all threads are equivalent.

In short: neither the current Delphi MM, neither the current string implementation scales well on multi-core CPU. You just found out a known issue of the current RTL. Read this SO question.

iOS Long Running Operation using GCD or NSThread?

If you needed to poll and you want to do it on a background thread, I might suggest a dispatch timer:

@property (nonatomic, strong) dispatch_source_t timer;

and you can then configure this timer to fire every two seconds:

dispatch_queue_t queue = dispatch_queue_create("com.domain.app.devicepoller", 0);
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
dispatch_source_set_timer(self.timer, dispatch_walltime(NULL, 0), 2.0 * NSEC_PER_SEC, 0.1 * NSEC_PER_SEC);
dispatch_source_set_event_handler(self.timer, ^{
[self pollDevice]; // this method should just poll the device and then return; no loop needed in this method
});
dispatch_resume(self.timer);

See the discussion of dispatch sources in the Concurrency Programming Guide. Also see the Grand Central Dispatch (GCD) Reference.

But most devices support some form of event-driven notification that obviates the need for any polling like this, but if you have to poll, and if you want to do it off the main thread, this is one way to do it.


The dispatch timer is useful when you want a timer to run on a background queue. If your vendor's API is responsive enough to run seamlessly in the main queue, then use NSTimer approach. It keeps you from having to do a lot of extra work to make your code thread-safe (e.g. proper synchronization as you update model objects, etc.). I assumed from your original question's thread-based approach that you had some reason to not use a timer and you had to move it to another thread for some reason, in which case I contend that a dispatch timer might be a better way to handle it than doing NSThread programming with a perpetual while loop. But if you (a) have to poll; and (b) don't have a compelling need to write multi-threaded code, then your life may be simpler if you used NSTimer-based approach.

Personally, I'd make sure I exhausted the Core Bluetooth approach before I pursued timers. App-level polling of a physical device should be considered the approach of last resort. Perhaps you can contact the vendor of the API and see if they have suggestions other than polling (because if they've been doing this for a while, they might have more elegant solutions that they can suggest).

Regarding receiving web service updates, again polling is horribly inefficient (and depending upon frequency of your polling, it can adversely affect battery life, consume cellular data plan, etc.). Push notifications might be the way to go if the server data is changing infrequently, but you want your app to be proactively notified of changes. Or if the server is really changing constantly, then maybe some socket based approach might make sense. But polling a network resource every two seconds (if that's what you meant to suggest) is rarely the right approach.

Just like polling of a physical device is the architecture of last resort, this is even more true for network communications. You really want to come up with an architecture that balances to device considerations with the business need. And if you conclude you have to do a polling-based approach, perhaps you might consider employing different polling frequency depending upon whether the user is on wifi vs. cellular.

Would calling Performance API frequently be causing a performance issue?

(V8 developer here.)

Calling performance.memory is pretty fast. You can easily verify that in a quick test yourself: just call it a thousand times in a loop and measure how long that takes.

[EDIT: Thanks to @Kaiido for highlighting that this kind of microbenchmark can in general be very misleading; for example the first operation could be much more expensive; or the benchmark scenario could be so different from the real application's scenario that the results don't carry over. Do keep in mind that writing useful microbenchmarks always requires some understanding/inspection of what's happening under the hood!

In this particular case, knowing a bit about how performance.memory works internally, the results of such a simple test are broadly accurate; however, as I explain below, they also don't matter.

--End of edit]

However, that observation is not enough to solve your problem. The reason why performance.memory is fast is also the reason why calling it frequently is pointless: it just returns a cached value, it doesn't actually do any work to measure memory consumption. (If it did, then calling it would be super slow.) Here is a quick test to demonstrate both of these points:





function f() {
if (!performance.memory) {
console.error("unsupported browser");
return;
}
let objects = [];
for (let i = 0; i < 100; i++) {
// We'd expect heap usage to increase by ~1MB per iteration.
objects.push(new Array(256000));
let before = performance.now();
let memory = performance.memory.usedJSHeapSize;
let after = performance.now();
console.log(`Took ${after - before} ms, result: ${memory}`);
}
}
f();

How to run a method in the background only when app is open and running?

What we did in our forms application was to make use of the Device.Timer and the Stopwatch class that available in System.Diagnostics, and Xamarin.Forms to create a very generic managed timer that we could interact with using the onStart, onSleep and onResume methods in Xamarin.Forms.

This particular solution doesn't require any special platform specific logic, and the device timer and stopwatch are non UI blocking.

using Xamarin.Forms;
using System;
using System.Linq;
using System.Diagnostics;

namespace YourNamespace
{
public partial class App : Application
{
private static Stopwatch stopWatch = new Stopwatch();
private const int defaultTimespan = 1;

protected override void OnStart()
{
// On start runs when your application launches from a closed state,

if (!stopWatch.IsRunning)
{
stopWatch.Start();
}

Device.StartTimer(new TimeSpan(0, 0, 1), () =>
{
// Logic for logging out if the device is inactive for a period of time.

if (stopWatch.IsRunning && stopWatch.Elapsed.Minutes >= defaultTimespan)
{
//prepare to perform your data pull here as we have hit the 1 minute mark

// Perform your long running operations here.

Device.InvokeOnMainThread(()=>{
// If you need to do anything with your UI, you need to wrap it in this.
});

stopwatch.Restart();
}

// Always return true as to keep our device timer running.
return true;
});
}

protected override void OnSleep()
{
// Ensure our stopwatch is reset so the elapsed time is 0.
stopWatch.Reset();
}

protected override void OnResume()
{
// App enters the foreground so start our stopwatch again.
stopWatch.Start();
}
}
}



Edit:

To give some context as to how the above solution works step by step:

The application starts from a closed state and the 'OnStart()' method creates our Device.Timer that ticks every second. It also starts our stopwatch that counts upto a minute.

When the app goes into the background it hits the 'OnSleep' method at this point if we were to pass a 'false' value into our Device.StartTimer() action it would not start up again. So instead we simply reset our stopwatch ready for when the app is opened again.

When the app comes back into the foreground it hits the 'OnResume' method, which simply starts the existing stopwatch.

2018 Edit:

This answer still has some merits even in 2018, but mainly for very specific situations. There are better platform specific ways to replicate this functionality even in Xamarin.Forms. The above still remains a platform agnostic way to perform a task after a period of time, taking into account user activity/inactivity.



Related Topics



Leave a reply



Submit