18.ISIS3510 Multi Threading

Download as pdf or txt
Download as pdf or txt
You are on page 1of 110

ISIS-3510

3.0
MULTI
THREADING
Why should we use multi-
threading in mobile
apps?
Mobile apps are prone to
performance bugs….
Mobile apps are prone to
performance bugs….
because of hardware
constraints and OS
GUI lagging and
Application Not
Responding Errors (ANRs)

PERFORMANCE BUGS
IN MOBILE APPS
Memory bloats and Out of
Memory Exceptions
(OOMs)
Android-related
Performance Issues

Resource Connection- Performance


Unclear DB-related GUI-related
leak related bad practices

96 64 6 47 4 33

Slow Eager Layout


Subopti Broadcast Service Suboptim Images
Memory connection Bulk data Inefficient GUI files
mal CPU receiver connection al usage operation
leak leak leak
(networking insert loading SQL lagging optimizati
of views s
usage issue) from db on
61 32 2 2 2 1 1 2 21 6 1 4

View leak Expensive GUI lag GUI Lagging


Strong Cursor Unneeded Activity due to Stream Energy Excessive Costly Animation operation because lagging when
Unneeded
(touch
reference leak elements leak missing leak comput. leak logging operation lagging in main of data scrolling
events
dismiss thread loading timing) 1 lists
6 3 6 7 2 3 15 7 2 8 3 2 3 5

Missing
Unneeded Unused condition
Unneeded Skippable
object Unneeded imports/ check before
activity function
instantiati variable dependen executing
instances operation
on cies 1 6
2 1 2 1

Costly High Expensive


Use of
date- frequency object
internal
formatting of repeated instantiatio
getters
operation operation n
2 1 1 1

Missing Inefficient Missing


Multithre- Suboptim Missed Objects Data Missing Missed data stream
ading String pre- al API structures
Compiler
operations
caching instantiation rendering batching represent
optimization
settings
related fetching usage opportunity in loop related optimization opportunity (not using
opportunity ation buffers)
11 5 1 1 4 2 6 3 3 6 4 1

Missed Inefficient Using


Using Using Inefficient
Threading multithrea synchroniza synchroni
ding tion among
inefficient dynamic operation
optimization zed
opportunity threads collection collection with buffers
collection
2 7 2 4 1 1 1
iOS-related
Performance Issues

Resource Connection- Performance


Unclear GUI-related
leak related bad practices

120 52 4 32 42

Issues Inefficient Issues


Subopti Slow Subopt. Layout
Memory with Network connection managem. painting GUI Images
mal CPU usage of files
leak sockets latency (networking of Web graphic lagging operations
usage library issues) views component
views optimiz.
110 9 1 1 2 7 7 6 3 15 4

Lagging Lagging
Reference Suboptimal Missed Missed Multithrea Inefficien. Objects Complex
Unreleased Unneded Costly String Compiler when because
cycle algorithmic caching batching ding- data instantiat. graphical
objects computat. operation operations settings scrolling of data
related complexity opportunity opportunity related represent. in loop effects
lists loading
53 15 6 1 2 7 4 2 12 5 1 1 9 2 2

Missing Missing
check Date- Missed
use of Unreleased Strong Unneeded Threading
condition formattin multithr.
auto pointers reference before threads optimizat.
g opportunity
release executing
39 14 9 2 1 1 2 2
GUI lagging and
ANRs

App process
Operatin
g Main / UI
system Thread
GUI lagging and
ANRs App process
Operatin
g Main / UI
system Thread

Mobile apps are single threaded by


default, and in the case of Flutter you can
not create more threads for being
executed within the same process
What is the difference
between a thread and a
process?
Process: it is an execution environment, that
has its own memory space (like a sandbox). A
process can not access to the memory space
of other process. Communication is then done
via IPC mechanism
Thread: a process has a main thread (like an
execution line), but more threads can be
started in the same process (except in Flutter).
Because the threads belong to a process, then,
those share the same memory space
GUI lagging and
ANRs OS App process Main

User action Low-level event Invoke app code


Low-level event App code
GUI lagging and
ANRs OS App process Main

User action Low-level event Invoke app code


Low-level event App code
GUI lagging and
ANRs OS App process Main

User action Low-level event Invoke app code


Low-level event App code

Computationally
expensive
operation
GUI lagging and
ANRs OS App process Main

User action Low-level event Invoke app code


Low-level event App code

Computationally
expensive
operation
GUI lagging and
ANRs OS App process Main

User action Low-level event Invoke app code


Low-level event App code
Another user action
Computationally
expensive
operation
GUI lagging and
ANRs OS App process Main

User action Low-level event Invoke app code


Low-level event App code
Another user action
Computationally
expensive
operation

There is an event loop, i.e., (dispatcher) and a queue


for events
GUI lagging and
ANRs OS App process Main

User action Low-level event Invoke app code


Low-level event App code
Another user action
Computationally
expensive
operation
GUI lagging and
ANRs OS App process Main

User action Low-level event Invoke app code


Low-level event App code
Another user action
Computationally
Response to expensive
UI is updated
First event operation
GUI lagging and
ANRs OS App process Main

User action Low-level event Invoke app code


Low-level event App code
Another user action
Computationally
Response to expensive
UI is updated
First event operation

During this time the app is not responsive or there is


GUI lagging and
ANRs OS App process Main

User action Low-level event Invoke app code


Low-level event App code
Another user action
Computationally
expensive
operation

Response to UI is updated
First event

If the response time is larger than 5 seconds


Hello word is not responding.
Do you want to close it?
GUI lagging and
ANRs OS App process Main

User action Low-level event Invoke app code


Low-level event App code
Another user action
Computationally
Response to expensive
UI is updated
First event operation

If the code is in a Broadcast Receiver, and the


execution takes more than 10 seconds…..
Hello word is not responding.
Do you want to close it?
How to avoid GUI lags
and ANRs?
Causes: I/O, access to remote resource,
complex computation, overdrawing, excessive
GC
General strategy: avoid blocking the main
thread with lengthy operations

Solutions: caching, remote delegation, GUI


optimization, memory management, multi-
threading, suspend functions, futures,
Multi-threading: use worker (background)
threads for lengthy tasks

WTn

Main

WT2

WT1
Async execution with Futures: futures are
executed when the main thread is free

Main
Future Future is
is executed on
created main thread

Main thread is busy


Main thread is free
Async execution with Futures: futures are
executed when the main thread is free

A future has three states:


- Uncompleted: the future has not started |
finished
- Completed with a value
- Completed with an error
Isolates (Flutter): an isolate is a process.
In Dart, your app can create different
isolates, and communication between
process (i.e., IPC) is done via messages

An isolated has its own memory space,


event loop, and main thread
How does multi-threading
work in iOS?
Unique API for handling multi-
threading in iOS apps

Sync and Async execution (threads)


GRAND CENTRAL
DISPATCH (GCD) Concurrent and Serial execution
(threads)

Predefined dispatch queues for


execution of tasks based on QoS
attributes
SERIAL

CONCURRENT
Work submitted to dispatch
queues executes on a pool of
threads managed by the system
SYNCHRONOUS The caller waits until the task
EXECUTION (SYNC) finishes

ASYNCHRONOUS The caller DOES NOT wait


EXECUTION (ASYNC) until the taks finishes
DISPATCH QUEUES

DispatchQueue.main

DispatchQueue.global(qos: .userInteractive)
DispatchQueue.global(qos: .userInitiated)
DispatchQueue.global(qos: .utility)
DispatchQueue.global(qos: .background)
DispatchQueue.global(qos: .unspecified)
DISPATCH QUEUES
Main thread,
DispatchQueue.main
serial

DispatchQueue.global(qos: .userInteractive)
DispatchQueue.global(qos: .userInitiated) Background,
DispatchQueue.global(qos: .utility) concurrent,
DispatchQueue.global(qos: .background) shared across
DispatchQueue.global(qos: .unspecified)
all apps
DispatchQueue.main.async {
// update ui here
}
IOS

DispatchQueue.global(qos: .background).async {
// do your job here

}
let main = DispatchQueue.main
IOS

let background = DispatchQueue.global()

let queue = DispatchQueue(label: “serial.example”)


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

// do your job here in background


IOS

DispatchQueue.main.async {
// update ui here
}

// more work here off the main thread

}
let delay = DispatchTime.now() + .seconds(120)
IOS

DispatchQueue.main.asyncAfter(deadline: delay) {
// Do your stuff
}
let queue = DispatchQueue(label: “serial.example”)
queue.sync {
// ...
}
IOS

DispatchQueue(label: “concurrent.example",
attributes: .concurrent).sync {
// . . .
}
How does multi-threading
work in Android (Java)?
Several options are available in Java for
multi-threading, which is both pro and cons at
the same time

Each of the available options need to be


carefully implemented depending on the use
case.

The easiest/safest way to use threads for


updating the ui is with AsyncTasks
android.os.AsyncTask <Param, Progress, Resu

This is intended for background workers that update


ANDROID

the UI, and run for short periods

It was the defacto option for multi-threading,


however, it is deprecated starting on Android R

The UI can be updated in 4 methods:


onPreExecute, doInbackGround,
onProgressUpdate, onPostExecute
https://developer.android.com/reference/android/os/AsyncTask.html
ANDROID

https://developer.android.com/reference/android/os/AsyncTask.html
ANDROID

https://developer.android.com/reference/android/os/AsyncTask.html
AsyncTask
argument
ANDROID

https://developer.android.com/reference/android/os/AsyncTask.html
ANDROID

https://developer.android.com/reference/android/os/AsyncTask.html
ANDROID

https://developer.android.com/reference/android/os/AsyncTask.html
ANDROID

Progress
value

https://developer.android.com/reference/android/os/AsyncTask.html
ANDROID

Result
value

https://developer.android.com/reference/android/os/AsyncTask.html
How does multi-threading
work in Kotlin?
Kotlin combines the multi-threading options
available in Android, with coroutines that
allows for suspending functions

The main idea with suspend functions is to


post-pone the execution, but, on the main
thread when it is free
Coroutines
Design Pattern -> Concurrency

You use them when you want to simplify code that


KOTLIN

executes asynchronously:

-Manage Long-running tasks, so they don’t block the


main thread

-Provide Main-safety. That allows to call any suspend


function from the main thread

https://developer.android.com/kotlin/coroutines
ANDROID Coroutines - Long Running task

0.4 seconds
KOTLIN

Main

Network
request on
main thread

https://medium.com/androiddevelopers/coroutines-on-android-part-i-getting-the-background-3e0e54d20bb
ANDROID Coroutines - Long Running task

0.4 seconds
KOTLIN

Main

Network
request on
main thread

The main thread is blocked while the


request is executed

https://medium.com/androiddevelopers/coroutines-on-android-part-i-getting-the-background-3e0e54d20bb
ANDROID Coroutines - Long Running task

0.4 seconds
KOTLIN

Main

Network
request on
main thread

How many CPU cycles were wasted? 1


billion cycles
(Pixel 2 CPU cycle -> 0.0000000004
seconds)
https://medium.com/androiddevelopers/coroutines-on-android-part-i-getting-the-background-3e0e54d20bb
If multi-threading is not
an option, then it is better
to avoid wasting CPU
cycles on the main thread
Coroutines - Long Running task
HTTP Request (off the main thread)
class ViewModel: ViewModel() {
fun fetchDocs() {
get("developer.android.com") { result ->
KOTLIN

show(result)
}
}
}

https://medium.com/androiddevelopers/coroutines-on-android-part-i-getting-the-background-3e0e54d20bb
Coroutines - Long Running task
HTTP Request (off the main thread)
class ViewModel: ViewModel() {
fun fetchDocs() {
get("developer.android.com") { result ->
KOTLIN

show(result)
}
}
}

HTTP Request using coroutines


suspend fun fetchDocs() {
val result = get("developer.android.com")
show(result)
}

suspend fun get(url: String) = withContext(Dispatchers.IO){/*...*/}

https://medium.com/androiddevelopers/coroutines-on-android-part-i-getting-the-background-3e0e54d20bb
Coroutines - Long Running task

HTTP Request using coroutines


suspend fun fetchDocs() {
val result = get("developer.android.com")
KOTLIN

show(result)
}

suspend fun get(url: String) = withContext(Dispatchers.IO){/*...*/}

•suspend — pause the execution of the current coroutine, saving all local variables
•resume — continue a suspended coroutine from the place it was paused
You can only suspend functions called by other suspend functions, or using a coroutine

https://medium.com/androiddevelopers/coroutines-on-android-part-i-getting-the-background-3e0e54d20bb
Coroutines - Long Running task
Callback
KOTLIN

Main
Thread

https://medium.com/androiddevelopers/coroutines-on-android-part-i-getting-the-background-3e0e54d20bb
Coroutines - Long Running task
Callback
KOTLIN

Main
Thread

https://medium.com/androiddevelopers/coroutines-on-android-part-i-getting-the-background-3e0e54d20bb
Coroutines - Long Running task
Callback
KOTLIN

Main
Thread

https://medium.com/androiddevelopers/coroutines-on-android-part-i-getting-the-background-3e0e54d20bb
Coroutines - Long Running task
Callback
KOTLIN

Main
Thread

class ViewModel: ViewModel() {


fun fetchDocs() {
get("developer.android.com")
{
result -> show(result)
}
}
}

https://medium.com/androiddevelopers/coroutines-on-android-part-i-getting-the-background-3e0e54d20bb
Coroutines - Long Running task
Callback
KOTLIN

Main
Thread

class ViewModel: ViewModel() {


fun fetchDocs() {
get("developer.android.com")
{
result -> show(result)
}
}
}

https://medium.com/androiddevelopers/coroutines-on-android-part-i-getting-the-background-3e0e54d20bb
Coroutines - Long Running task

Callback Coroutine
KOTLIN

Main
Thread

class ViewModel: ViewModel() {


fun fetchDocs() {
get("developer.android.com")
{
result -> show(result)
}
}
}

https://medium.com/androiddevelopers/coroutines-on-android-part-i-getting-the-background-3e0e54d20bb
Coroutines - Main Safety
Coroutines will run on the main thread by default,
and suspend does not mean background.
KOTLIN

Dispatchers are in charge of running the coroutines code.


They define if the coroutine runs in the main thread or in other thread

https://medium.com/androiddevelopers/coroutines-on-android-part-i-getting-the-background-3e0e54d20bb
KOTLIN Coroutines - Main Safety

suspend fun fetchDocs() {


val result = get("developer.android.com")
show(result)
}

https://medium.com/androiddevelopers/coroutines-on-android-part-i-getting-the-background-3e0e54d20bb
KOTLIN Coroutines - Main Safety

suspend fun fetchDocs() {


val result = get("developer.android.com")
show(result)
}

suspend fun get(url: String) =


withContext(Dispatchers.IO) {
/* perform blocking network IO here */
}

https://medium.com/androiddevelopers/coroutines-on-android-part-i-getting-the-background-3e0e54d20bb
KOTLIN Coroutines - Main Safety

suspend fun fetchDocs() {


val result = get("developer.android.com")
show(result)
}

suspend fun get(url: String) =


withContext(Dispatchers.IO) {
/* perform blocking network IO here */
}

https://medium.com/androiddevelopers/coroutines-on-android-part-i-getting-the-background-3e0e54d20bb
How to run a
Coroutine?
Coroutines
You can start coroutines in one of two ways:
launch
async
Starts a new coroutine and
Starts a new coroutine and allows
KOTLIN

doesn't return the result to the


you to return a result with a
caller. Any work that is
suspend function called await.
considered "fire and forget" can
be started using launch.

https://developer.android.com/kotlin/coroutines
launch is a bridge from regular functions into
coroutines
Coroutines
You can start coroutines in one of two ways:
launch
async
Starts a new coroutine and
Starts a new coroutine and allows
KOTLIN

doesn't return the result to the


you to return a result with a
caller. Any work that is
suspend function called await.
considered "fire and forget" can
be started using launch.

https://developer.android.com/kotlin/coroutines
Coroutines
You can start coroutines in one of two ways:
launch
async
Starts a new coroutine and
Starts a new coroutine and allows
KOTLIN

doesn't return the result to the


you to return a result with a
caller. Any work that is
suspend function called await.
considered "fire and forget" can
be started using launch.
?

https://developer.android.com/kotlin/coroutines
Coroutines scopes

viewModelScope:While the view


KOTLIN

model is active

lifeCycleScope: Activity/fragment life


cycle
KOTLIN Coroutines

You can
modify UI after
doing a
background
work without
doing to many
steps

https://developer.android.com/kotlin/coroutines
Coroutines

Possible Problems:
KOTLIN

-Leaked Work

-Unable to keep track

-Problems handling
errors

https://developer.android.com/kotlin/coroutines
Coroutines

Possible Problems:
KOTLIN

-Leaked Work.

-Unable to keep track.

-Problems handling
errors

https://medium.com/androiddevelopers/coroutines-on-android-part-ii-getting-started-3bff117176dd
Coroutines

Check these links out:


KOTLIN

Theory:
https://developer.android.com/kotlin/coroutines

https://medium.com/androiddevelopers/coroutines-on-android-part-i-getting-the-background-3e0e54d20bb

https://medium.com/androiddevelopers/coroutines-on-android-part-ii-getting-started-3bff117176dd

Practical Examples:
https://medium.com/androiddevelopers/coroutines-on-android-part-iii-real-work-2ba8a2ec2f45
And what about
flutter?
Flutter is single-threaded, therefore, multi-
threading is not available. Therefore, to avoid
blocking the main thread, async functions
should be used.

Multi-threading can be enabled by using


FLUTTER Futures

What is the output? Type


the snippet in http://
dartpad.dev
FLUTTER Futures
Why ?
Because a future is
code block expected
to run in the future
(when the main
thread is free) and
without blocking the
main thread
FLUTTER Futures

The print is executed first because the


future in the fetchUserOrder is
delayed
FLUTTER Futures

Try now with a future that is not


delayed
FLUTTER Futures
FLUTTER Futures

What is the output?


FLUTTER Futures
FLUTTER Futures

At this point the main thread is


free.. So the future is executed
Futures
Let’s return the value now for
being printed at the main
function
FLUTTER
FLUTTER Futures

What is the output?


FLUTTER Futures
Why ?
Because the future is
in state
uncompleted
Therefore, you need to:

- Implemente a “then” block for the


future (non blocking option)

- Wait for a value using the async


and await keywords (this blocks
the main thread)
FLUTTER Futures
FLUTTER Futures
FLUTTER Futures
FLUTTER Futures

The code to
be executed
when the
value is
returned
What about the
async/await way for
futures?
Futures
Let’s go back to a previous example
FLUTTER
Futures
To make it work (blocking the main thread) we need to use async/
await
FLUTTER
FLUTTER Futures

•To define an async function,


add async before the function
body

•The await keyword works only


in async functions

You might also like