Table of Contents

20.6.0 Task

Imagine the following situation:

In a programme, time-intensive computations are executed as a subtask in a procedure P. This means that the interpreter is bound while P is being executed. During the time P is being executed, it does not enter the event loop, which can be regarded as the interpreter's rest mode. There it waits for external events, such as a click on a button. This means - while P is being executed - that the GUI of the process is frozen!

In order to still be able to react promptly to an event, there are two possibilities:

In Gambas, tasks are also used, for example in the gb.form component, to generate a file preview. The trick is this: while the file preview is being generated in a task, the main programme can still interact with the user. It is not in a time-consuming routine and the event loop in the main program runs normally.

A task represents a copy of the parent process (fork → http://de.wikipedia.org/wiki/Fork_%28Unix%29) and runs as an independent process with its own process number completely independently of the calling parent process. Passed variables, for example, can therefore be changed independently in the task without the parent (parent) process noticing anything. Of course, the same applies in the other direction.

In this chapter, in addition to the properties, methods and events of the class Task (gb), you will be introduced to projects that show their use.

20.6.0.1 Properties

The Task class has three properties:

PropertyData typeDescription
HandleIntegerReturns the process number (PID) of the background process (task).
RunningBooleanReturns True if the specified background process is running.
ValueVariantReturns the function value of the Main() function of the background process. If an error occurred in the background process, an error is triggered which can be read and evaluated via Task.Value.

Table 20.6.0.1.1 : Properties of the class Task

20.6.0.2 Methods

The Task class has only two methods:

20.6.0.3 Events

In the Task class, these three events are declared:

EventDescription
Read(Data As String)The event is triggered when the task outputs data (data type String) to its standard output. Normally, the data is output line by line; unless the task uses the FLUSH statement without an argument. The task standard output can also be used to send task status information to the parent process.
Error(Data As String)The event is triggered when the task outputs data on its error output.
Kill()The event is triggered when the active task has been terminated as a background process.

Table 20.6.0.3.1 : Events of the Class Task

The event Error(Data AS String) is only triggered if data was written to the standard error output in the background process, for example with the ERROR command!

Contrary to what the event Task_Kill() suggests, the event is not triggered when the task object is destroyed, but when the task process is terminated. Terminated here means:

It is important to emphasise the difference between Task as a background process - in which the Task code is executed - and Task object, which exists in the parent Gambas process. The Task object allows you to communicate with the Task as long as the Task exists. The Task object, however, 'survives' the end of the background process so that you can still read the function value of the Main() function (data type Variant) via the Task.Value property or other properties after it ends.

20.6.0.4 Task as background process in Gambas

To have a task processed in the background in a task you need to:

The question of whether a task is necessarily started as a background process when a task object is created can be answered like this:

20.6.0.5 Task Priority

You can change the priority of a background process via the property Application.Priority (data type Integer) → chapter 20.11 Class Application (gb). The default priority has the value 0.

To increase the priority of a background process (values -1 to -20), root rights are required, which you do not need to decrease the priority (values 1 to 19).

' Gambas class file
 
Inherits Task
 
Public Function Main() As Variant
  Dim aTagesListe As String[]
 
  Application.Priority = 10 '-- Process priority for the task
 
  aTagesListe = Split("Sonntag,Montag,Dienstag,Mittwoch,Donnerstag,Freitag,Samstag", ",")
  Return aTagesListe[WeekDay(Now())]
 
End

20.6.0.6 Interactive Process Communication

Data transfer between a task as a background process and the parent (parent) process is only uni-directional - i.e. in one direction - in the Task (gb) class:

(A) Programme → Task

In this direction, you can give the task one-time arguments as start values. To do this, you must define public variables in the task class - here MyTask.class - in order to assign suitable (start) values to them in the programme immediately after creating the task.

MyTask.class:

  Public iWaitTime As Integer '-- Start-Argument für die Wartezeit zwischen den drei Anzeigen

FMain.class:

Private hTask As MyTask
 
Public Sub btnTaskStart_Click()
  If hTask = NULL then hTask = New MyTask As "MyTask" ' = Task-Klassen-Name
  hTask.iWaitTime = 3 ' Wertzuweisung für globale Variable in der Klasse MyTask
  ...
 
End

You can see another approach to passing arguments here:

MyTask.class:

Public Sub _new({Matrix} As Matrix, Row As Integer)
  $hMatrix = {Matrix}
  $iRow = Row
End

FMain.class:

Public Sub Main()
  Dim iRow, iCol As Integer
  Dim hDeterminante As TaskMinors
 
  ...
  For iCol = 0 To $hMatrix.Width - 1
      hDeterminante = New TaskMinors($hMatrix, iRow) As "TaskMinors"
      hDeterminante.Tag = iRow
      Inc $iTasks '-- Den (internen) Zähler für die gestarteten Tasks erhöhen
  Next
 
  ...
End

(B) Task → Programme

The type of data transfer is determined by whether a (function) value is only returned once from the task or whether data is permanently transferred from the task to the main process (clocked, random). If you need data from the running task, you must read this data sent to the standard outputs in the event Task_Read(Data As String) or in the event Task_Error(Data As String) via the PRINT or ERROR command in the Main() method. Note: In the second case, only data of the data type String is allowed.

In the projects presented you will find the different implementations of inter-process communication (IPC).

For a permanent data transmission Task → Programme of data with native data type, Tobias Boege has developed serialisation and deserialisation functions, which are presented in a special project.

20.6.0.7 Project 1

The task to be worked on in project 1 does not sound spectacular: With the start of the programme, an analogue clock is to be displayed and parallel to this, the day of the week is calculated and displayed from the current date via a task. Since you need the function value of the Main() function from the task - which is its only purpose in life - once, you can read out the property Task.Value after the background process has ended.

The source code for the Task class DayTask.class is given in full, while only relevant sections of the class FMain.class are presented:

' Gambas class file
 
Inherits Task
 
Public Function Main() As Variant
  Dim aTagesListe As String[]
 
  aTagesListe = Split("Sonntag,Montag,Dienstag,Mittwoch,Donnerstag,Freitag,Samstag", ",")
  Return aTagesListe[WeekDay(Now())] '-- Return of the day of the week
 
End

In this class, the day of the week is calculated from the current date and returned as a function value from the Main method.

In the programme, a task is created in the procedure TaskRun():

  ' Gambas class file
 
  Private $hTask As DayTask
 
  Public Sub Form_Open()
    ...
'-- Creating a Task: Task Object and Task Process
    TaskRun()
 
  End
 
  Private Sub TaskRun()
 
'-- Create a new task object - Object name = Object event name: DayTask
    If $hTask = Null Then $hTask = New DayTask As "DayTask"
    Repeat
      Wait 0.001
    Until $hTask <> Null
 
  End
 
  Public Sub DayTask_Kill()
    Dim DayOfWeek As String
    Dim sErrorMessage As String
 
'-- Save process return value. Alternative: Last.Value
    Try DayOfWeek = $hTask.Value
    If Not Error Then
       lblDayOfWeek.Text = DayOfWeek
    Else
       sErrorMessage = "Error!" & gb.NewLine
       sErrorMessage &= Error.Where & gb.NewLine
       sErrorMessage &= Error.Text
       Message.Error(sErrorMessage)
       lblDayOfWeek.Text = "Task error"
    Endif
 
  End
...

The analogue clock is displayed and below it the weekday calculated once in the task for the current date - if no error occurred in the task:

Figure 20.6.0.7.1: Display of time and weekday

You can create an error quite easily by using a semicolon instead of the correct separator comma, for example:

  aTagesListe = Split("Sonntag,Montag,Dienstag,Mittwoch,Donnerstag,Freitag,Samstag", ";")

The display changes in the lower part because in the procedure 'Public Sub DayTask_Kill()' the error is detected and documented:

Figure 20.6.0.7.2: Display of time and error message

You can see very clearly again in the last case that with the creation of the task after the fork into two independent processes, the clock runs completely independent of the task and its return value.

Download

Chapter & Project

Download