Dart Isolate (Threading in dart)


What are Threads in Computer Processor or CPU?

Threads are the virtual components or codes, which divides the physical core of a CPU into virtual multiple cores. A single CPU core can have up-to 2 threads per core.

For example, if a CPU is dual core (i.e., 2 cores) it will have 4 threads. And if a CPU is Octal core (i.e., 8 core) it will have 16 threads and vice-versa.

Working:

The thread is created by a process. Every time you open an application, it itself creates a thread which will handle all the tasks of that specific application. Like-wise the more application you open more threads will be created.

Dart Isolate: 



Dart is a single-thread language. So we are not using threads here we are use isolates here. Dart allows us to asynchronous programming which runs our program without getting blocked. The asynchronous programming is used to achieve concurrency. Dart isolate is a version of the thread. But there is key difference between the common implementation of "Thread" or "Isolates". The isolate works differently in comparison of Thread. The isolates are independent workers that do not share memory, but instead interconnect by passing message over channels. Since isolates completes its task by passing message thus it need a way to serialize a message.

The communication between the isolates is done by the message passing as a client and server. It helps the program to take advantage of multicore microprocessor out of the box.

Dart provides the dart:isolate package to apply the isolate in our program. It provides the solution to taking single-threaded Dart code and allowing application to make greater use of the hardware available.

Create and Start an Isolate

Dart provides the spawn() method to create an isolate. It must be declared with an 'entry point' with a single parameter. This parameter displays a port which isolate use to refer back notification message.

Let's understand the following example -

Example - 1 

  1. import 'dart:isolate';    
  2. void sayhii(var msg){   
  3.    print('execution message is :${msg}');   
  4. }    
  5. void main(){   
  6.    Isolate.spawn(sayhii,'Hello!!');   
  7.    Isolate.spawn(sayhii,'Whats up!!');   
  8.    Isolate.spawn(sayhii,'Welcome!!');   
  9.      
  10.    print('execution from main1');   
  11.    print('execution from main2');   
  12.    print('execution from main3');   
  13. }  

Output in different-different times



Explanation:

In the above program, the spawn method of the isolate class executed a function sayhii in parallel of remaining code. It takes two parameters.

The function that we want to spawned and the string that will be passed to the spawned function.

We have two functions sayhii() and main() function might not run in the same order each time. If you run the above program, then the output will be different each time as we can see in second output.

And many times when we are run this code sometimes we see that Welocme!! and Hello!! printed and sometimes Welcome!! and Whats up!! printed. this is context switching.

Note - We can also pass NULL value if there is no object to pass in spawned function.

context switching:

Context switching is a technique or method used by the operating system to switch a process from one state to another to execute its function using CPUs in the system. When switching perform in the system, it stores the old running process's status in the form of registers and assigns the CPU to a new process to execute its tasks. While a new process is running in the system, the previous process must wait in a ready queue. The execution of the old process starts at that point where another process stopped it. It defines the characteristics of a multitasking operating system in which multiple processes shared the same CPU to perform multiple tasks without the need for additional processors in the system.

Example - 2

  1. // Start the isolate
  2. void start() async {
  3.   ReceivePort receiverPort =
  4.       ReceivePort(); // Port for isolate to receive message.
  5.   isolate = await Isolate.spawn(runTimer, receiverPort.sendPort);
  6.   receiverPort.listen((data) {
  7.     stdout.write('Receiving: ' + data + ', ');
  8.   });
  9. }

  10. void runTimer(sendPort) {
  11.   int count = 0;
  12.   Timer.periodic(new Duration(seconds: 1), (Timer t) {
  13.     count++;
  14.     String msg = 'notification ' + count.toString();
  15.     stdout.write('Sending: ' + msg + ' -');
  16.     sendPort.send(msg);
  17.   });
  18. }

Explanation:

In the above code, we created an asynchronous method start() which creates a port and spawn an isolate. We signified the start method as async because of wan can await the response from the spawning of the isolates and to store a reference to the new isolate. It is essential when we want to kill the running isolates. We passed the two parameters in the spawn() method, the first parameter runTimer method, that is a callback function to execute runTimer() and second parameter sendPort which is a callback function and it will used to send message back to the caller. The start() method starts listening the receiverPort for message from isolate. Once it will receive the message then it will print as a console output.

The runTimer() method begins a timer that fires every second in order to update a counter. It sends a notification message via the port which it received when the isolate was spawned.

Stop an Isolate

The dart: isolates package provides the kill() method which is used to stop a running isolate.

Let's understand the following example.

Example -

  1. // Stopping the isolate using the stop() function.
  2. void stop() {
  3.   if (isolate != null) {
  4.     stdout.writeln('Stopping Isolate.....');
  5.     isolate.kill(priority: Isolate.immediate);
  6.     isolate = null;
  7.   }
  8. }

Explanation:

In the above example, we have declared a stop() method that will destroy the running isolate and sets its reference to null. We defined the priority of isolate: immediate that will terminate the isolate as soon as.

Complete Program

  1. import 'dart:io';
  2. import 'dart:async';
  3. import 'dart:isolate';

  4. Isolate isolate;

  5. // Start the isolate
  6. void start() async {
  7.   ReceivePort receiverPort =
  8.       ReceivePort(); // Port for isolate to receive message.
  9.   isolate = await Isolate.spawn(runTimer, receiverPort.sendPort);
  10.   receiverPort.listen((data) {
  11.     stdout.write('Receiving: ' + data + ', ');
  12.   });
  13. }

  14. void runTimer(sendPort) {
  15.   int count = 0;
  16.   Timer.periodic(new Duration(seconds: 1), (Timer t) {
  17.     count++;
  18.     String msg = 'notification ' + count.toString();
  19.     stdout.write('Sending: ' + msg + ' -');
  20.     sendPort.send(msg);
  21.   });
  22. }

  23. // Stopping the isolate using the stop() function.
  24. void stop() {
  25.   if (isolate != null) {
  26.     stdout.writeln('Stopping Isolate.....');
  27.     isolate.kill(priority: Isolate.immediate);
  28.     isolate = null;
  29.   }
  30. }

  31. void main() async {
  32.   stdout.writeln('Starting Isolate...');
  33.   await start();
  34.   stdout.writeln('press enter key to quit');
  35.   await stdin.first;
  36.   stop();
  37.   stdout.writeln('Bye!');
  38.   exit(0);
  39. }

Output:



Example: 2

import 'dart:convert';
import 'dart:isolate';

import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  State createState() => _MyAppState();
}

class _MyAppState extends State {
  var count = 0;
  var apiData = '';

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: SafeArea(
        child: Scaffold(
          backgroundColor: Colors.white,
          body: Center(
            child: Column(
              children: [
                TextButton(
                  onPressed: () {
                    Isolate.spawn((a) async {
                      for (var i = 0; i < 20000; i++) {
                        print(i);
                      }
                    }, 20000);
                  },
                  child: Text(
                    'start1',
                  ),
                ),
                TextButton(
                  onPressed: () async {
                    setState(() {
                      apiData = "Start";
                    });
                    ReceivePort _port = ReceivePort();
                    Isolate.spawn((a) async {
                      a.send("Loading...");
                      Uri numberAPIURL =
                          Uri.parse('http://numbersapi.com/random/trivia?json');
                      final response = await Dio().getUri(numberAPIURL);
                      if (response.statusCode == 200) {
                        a.send(response.data.toString());
                      } else {
                        a.send('Failed to fetch number trivia');
                      }
                    }, _port.sendPort);
                    _port.listen((message) {
                      setState(() {
                        apiData = message;
                        print(message);
                      });
                    });
                  },
                  child: Text(
                    'start2 $apiData',
                  ),
                ),
                TextButton(
                  onPressed: () {
                    setState(() {
                      count++;
                    });
                  },
                  child: Text(
                    'count $count',
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}



Post a Comment

Previous Post Next Post

Subscribe Us


Get tutorials, Flutter news and other exclusive content delivered to your inbox. Join 1000+ growth-oriented Flutter developers subscribed to the newsletter

100% value, 0% spam. Unsubscribe anytime