Auch SAP kann Multitasking. Dazu muss mann einen RFC-fähigen Funktionsbaustein in einem neuen Task innerhalb einer sogenannten Taskgroup aufrufen. Die Abarbeitung erfolgt dann asynchron, dass Ergebnis wird in einer Handler-Funktion zurückgeliefert.
Im folgenden Funktionsbeispiel erzeugt das Rahmenprogramm „zparallel“ Arbeitspakete (it_input) für die Tasks und ein Objekt der Klasse „lcl_task_manager“. Letzteres managed die Verteilung der Eingangsdaten (it_input) auf mehrere Tasks. Die Taskobjekte werden durch die Klasse „lcl_task“ abgebildet. Die einzelnen Tasks rufen asynchron mit ihren Arbeitspaketen den RFC-fähigen Funktionsbaustein „BAPI_BILLINGDOC_GETLIST“.
Nach erfolgreichem Beenden aller Tasks werden die Ergebnisse der Arbeitspakete zusammengesetzt (it_result) und an das Rahmenprogramm zurückgegeben.
Weiterführende Informationen: Link, Link, Link und Link
Rahmenprogramm: REPORT zparallel (Aufruf der Parallelverarbeitung)
REPORT zparallel.
* Klassen für Taskmanagement
INCLUDE ztask_manager.
* iTabs für Eingabe- und Ausgabedaten
DATA: it_input TYPE lcl_task=>ty_it_input_data.
DATA: it_result TYPE lcl_task_manager=>ty_it_final_result.
DATA: it_statistic TYPE lcl_task_manager=>ty_it_statistic.
PARAMETERS: p_rows TYPE i DEFAULT 1000. " Test-Elemente
PARAMETERS: p_wait TYPE i DEFAULT 5. " Wartezeit in [s]
PARAMETERS: p_max_rt TYPE i DEFAULT 3. " max. Anzahl Versuche
PARAMETERS: p_size TYPE i DEFAULT 100. " Intervallgröße
START-OF-SELECTION.
DATA: it_vbelv TYPE STANDARD TABLE OF vbelv WITH DEFAULT KEY.
* Intervalle erzeugen
SELECT DISTINCT vbelv FROM vbfa INTO TABLE @it_vbelv UP TO @p_rows ROWS.
IF sy-subrc = 0.
SORT: it_vbelv BY table_line.
DATA(lv_lines) = lines( it_vbelv ).
DATA(lv_parts) = lv_lines DIV p_size.
DATA(lv_mod) = lv_lines MOD p_size.
IF lv_mod <> 0.
lv_parts = lv_parts + 1.
ENDIF.
DATA(lv_idx_l) = 0.
DATA(lv_idx_h) = 0.
DO lv_parts TIMES.
lv_idx_l = lv_idx_h + 1.
lv_idx_h = lv_idx_l - 1 + p_size.
IF lv_idx_h > lv_lines.
lv_idx_h = lv_lines.
ENDIF.
APPEND VALUE #( sign = 'I'
option = 'BT'
ref_doc_low = it_vbelv[ lv_idx_l ]
ref_doc_high = it_vbelv[ lv_idx_h ] ) TO it_input.
ENDDO.
* Referenz auf Tastmanager für Parallelverarbeitung
DATA(o_task_manager) = lcl_task_manager=>get_instance( i_task_group = lcl_task_manager=>co_default_task_group ).
DATA: lv_started TYPE i.
DATA: lv_ended TYPE i.
DATA: lv_missed TYPE i.
* Timer starten
DATA(o_timer) = cl_abap_runtime=>create_hr_timer( ).
DATA(usec_start) = o_timer->get_runtime( ).
* Parallele Verarbeitung der Eingabedaten starten
IF abap_true = o_task_manager->start_parallel_working( EXPORTING
i_it_input_data = it_input
i_wait_time_sec = p_wait
i_max_retries = p_max_rt
IMPORTING
e_it_final_result = it_result
e_it_statistic = it_statistic
e_tasks_started = lv_started
e_tasks_ended = lv_ended
e_tasks_missed = lv_missed ).
* Timer stoppen
DATA(usec_end) = o_timer->get_runtime( ).
DATA(usec) = CONV decfloat16( usec_end - usec_start ).
DATA(sec) = usec / 1000000.
WRITE: / ' Zeit [µs]:', usec.
WRITE: / ' Zeit [s]:', sec.
WRITE: / ' max. Tasks:', o_task_manager->get_max_tasks( ).
WRITE: / ' free Tasks:', o_task_manager->get_free_tasks( ).
WRITE: / 'Tasks started:', lv_started.
WRITE: / ' Tasks ended:', lv_ended.
WRITE: / ' Tasks missed:', lv_missed.
WRITE: / ' Input lines:', lines( it_input ).
WRITE: / ' Result lines:', lines( it_result ).
ULINE.
WRITE: / | Task # \| Retries \| Time [µs] \| S \| IN: LOW \| IN: HIGH \| SUBRC \| SIZE|.
ULINE.
LOOP AT it_statistic ASSIGNING FIELD-SYMBOL(<fs_stat>).
WRITE: / <fs_stat>-task_number, '|', <fs_stat>-retries, '|', <fs_stat>-runtime, '|', <fs_stat>-successful, '|', <fs_stat>-input_data-ref_doc_low, '|', <fs_stat>-input_data-ref_doc_high, '|', <fs_stat>-subrc, '|', <fs_stat>-output_part_size.
ENDLOOP.
ULINE.
WRITE: / | ITEM # \| IN: LOW \| IN: HIGH \| OUT: REF_DOC|.
ULINE.
* Ergebnisausgabe
LOOP AT it_result ASSIGNING FIELD-SYMBOL(<fs_res>).
WRITE: / sy-tabix, '|', <fs_res>-input_data-ref_doc_low, '|', <fs_res>-input_data-ref_doc_high, '|', <fs_res>-output_data-ref_doc.
ENDLOOP.
ENDIF.
ENDIF.
Task-Klassen: INCLUDE ZTASK_MANAGER
*--------------------------------------------------------------------*
* Task-Klasse (Worker)
*--------------------------------------------------------------------*
* Ruft asynchon einen RFC-fähigen Funktionsbaustein
* und verarbeitet asynchron das Ergebnis (Event)
*--------------------------------------------------------------------*
CLASS lcl_task DEFINITION FINAL.
PUBLIC SECTION.
* Eingabedaten des Task
TYPES: ty_input_data TYPE bapi_ref_doc_range.
* Ausgabedaten des Task
TYPES: ty_output_data TYPE bapivbrksuccess.
TYPES: ty_it_input_data TYPE STANDARD TABLE OF ty_input_data WITH DEFAULT KEY.
TYPES: ty_it_output_data TYPE STANDARD TABLE OF ty_output_data WITH DEFAULT KEY.
* Name des RFC-fähigen Funktionsbausteines für "start_async_call" und "task_receive"-Handler
CONSTANTS: co_fb_name TYPE string VALUE 'BAPI_BILLINGDOC_GETLIST'.
* Event, wird getriggert, wenn die asynchrone Methode task_receive aufgerufen wird
EVENTS:
data_received
EXPORTING
VALUE(e_subrc) TYPE sy-subrc
VALUE(e_runtime) TYPE int4
VALUE(e_retries) TYPE i
VALUE(e_input_data) TYPE ty_input_data
VALUE(e_output_data) TYPE ty_it_output_data.
* Konstruktor
METHODS:
constructor
IMPORTING
i_task_group TYPE rzllitab-classname
i_task_number TYPE i.
* Startet asynchronen Aufruf des RFC-Bausteins
METHODS:
start_async_call
IMPORTING
i_input_data TYPE ty_input_data
i_started_after_n_retries TYPE i
RETURNING VALUE(rv_subrc) TYPE sy-subrc.
* Handlermethode, wird asynchron gerufen
METHODS:
task_receive
IMPORTING
p_task TYPE clike.
* Hilfsfunktionen
METHODS:
is_running
RETURNING VALUE(rv_running) TYPE boolean.
METHODS:
get_group
RETURNING VALUE(rv_group) TYPE rzllitab-classname.
METHODS:
get_task_number
RETURNING VALUE(rv_number) TYPE i.
PRIVATE SECTION.
DATA: lv_group TYPE rzllitab-classname.
DATA: lv_number TYPE i.
DATA: lv_running TYPE boolean.
DATA: lv_started_after_n_retries TYPE i.
DATA: lv_start_time TYPE int4.
DATA: lv_end_time TYPE int4.
* Merker für Übergabe-Parameter in start_async_call
DATA: lv_input_data TYPE ty_input_data.
ENDCLASS.
CLASS lcl_task IMPLEMENTATION.
*--------------------------------------------------------------------*
* Konstruktor
*--------------------------------------------------------------------*
* -> i_task_group - Gruppenname
* -> i_task_number - Tasknummer
*--------------------------------------------------------------------*
METHOD constructor.
lv_group = i_task_group.
lv_number = i_task_number.
lv_running = abap_false.
lv_start_time = 0.
lv_end_time = 0.
lv_started_after_n_retries = 0.
ENDMETHOD.
*--------------------------------------------------------------------*
* Startet asynchronen Aufruf des RFC-Bausteins
*--------------------------------------------------------------------*
* -> i_input_data - Eingabedaten für Verarbeitung
* -> i_started_after_n_retries - Statistikwert, nach wieviel Versuchen
* der Task gestartet werden konnte
* <- rv_subrc - Status des asynchronen Aufrufs
*--------------------------------------------------------------------*
METHOD start_async_call.
lv_running = abap_false.
lv_started_after_n_retries = i_started_after_n_retries.
GET RUN TIME FIELD lv_start_time.
* neuen asynchronen Aufruf des Bausteines starten
* Gruppe, Tasknummer und Handlerfunktion übergeben
* Baustein muss RFC-fähig sein
* Verfügbare RFC-Gruppen für Parallelverarbeitung: Transaktion RZ12
* Namen und Typen der EXPORTING-Parameter beachten:
* -> fehlerhafte Angaben werden nicht vom Compiler bemerkt oder auch nicht als sy-subrc zurückgegeben
CALL FUNCTION co_fb_name
STARTING NEW TASK lv_number
DESTINATION IN GROUP lv_group
CALLING task_receive ON END OF TASK
EXPORTING
refdocrange = i_input_data
EXCEPTIONS
system_failure = 1
communication_failure = 2
resource_failure = 3
OTHERS = 4.
* Fehlerstatus zurückgeben
rv_subrc = sy-subrc.
IF rv_subrc = 0.
lv_input_data = i_input_data.
lv_running = abap_true.
ENDIF.
ENDMETHOD.
*--------------------------------------------------------------------*
* Receive-Funktion, welche bei Fertigsstellung vom Task asynchron
* getriggert wird
* Es werden keine WRITE-Statements ausgeführt!
*--------------------------------------------------------------------*
* -> p_task - Tasknummer
*--------------------------------------------------------------------*
METHOD task_receive.
DATA: it_task_data TYPE ty_it_output_data.
* Ergebnisse des asynchronen RFC-Aufrufes vom Funktionsbaustein holen
RECEIVE RESULTS FROM FUNCTION co_fb_name
TABLES
success = it_task_data
EXCEPTIONS
system_failure = 1
communication_failure = 2
resource_failure = 3
OTHERS = 4.
GET RUN TIME FIELD lv_end_time.
DATA(lv_runtime) = lv_end_time - lv_start_time.
* Event für Datenrückgabe triggern
RAISE EVENT data_received
EXPORTING
e_subrc = sy-subrc
e_runtime = lv_runtime
e_retries = lv_started_after_n_retries
e_input_data = lv_input_data
e_output_data = it_task_data.
lv_running = abap_false.
ENDMETHOD.
*--------------------------------------------------------------------*
* Gibt Status des Tasks
*--------------------------------------------------------------------*
* <- rv_running - Status des Tasks
*--------------------------------------------------------------------*
METHOD is_running.
rv_running = lv_running.
ENDMETHOD.
*--------------------------------------------------------------------*
* Gibt Gruppenname, in welcher der Task läuft
*--------------------------------------------------------------------*
* <- rv_group - Gruppenname
*--------------------------------------------------------------------*
METHOD get_group.
rv_group = lv_group.
ENDMETHOD.
*--------------------------------------------------------------------*
* Gibt Tasknummer
*--------------------------------------------------------------------*
* <- rv_number - Tasknummer
*--------------------------------------------------------------------*
METHOD get_task_number.
rv_number = lv_number.
ENDMETHOD.
ENDCLASS.
*--------------------------------------------------------------------*
* Verwaltungs-Klasse für Tasks
*--------------------------------------------------------------------*
* 1. Verarbeitet Eingabedaten (Workitems)
* 2. Holt für jedes Workitem einen freien Task
* 3. Startet pro Workitem einen Task (asynchron)
* 4. Wertet das asynchron gelieferte Ergebis in einer Handlermethode aus
*--------------------------------------------------------------------*
* Sinnvoll ist die Implementierung nur, wenn die Workprozesse
* ausreichend lange dauern, so dass sich eine parallele Verarbeitung
* überhaupt lohnt
*--------------------------------------------------------------------*
CLASS lcl_task_manager DEFINITION FINAL CREATE PRIVATE.
PUBLIC SECTION.
* Datentyp für finale Ausgabe, wenn alle Workprozesse fertig sind
TYPES: BEGIN OF ty_final_result,
input_data TYPE lcl_task=>ty_input_data,
output_data TYPE lcl_task=>ty_output_data,
END OF ty_final_result.
TYPES: ty_it_final_result TYPE STANDARD TABLE OF ty_final_result WITH DEFAULT KEY.
TYPES: BEGIN OF ty_statistic,
task_number TYPE i,
runtime TYPE int4,
retries type i,
input_data TYPE lcl_task=>ty_input_data,
output_part_size TYPE i,
subrc TYPE sy-subrc,
successful TYPE boolean,
END OF ty_statistic.
TYPES: ty_it_statistic TYPE STANDARD TABLE OF ty_statistic WITH DEFAULT KEY.
* RFC-Gruppenname
* die Gruppe mit classname = 'parallel_generators' wird standardmäßig von Admins eingerichtet
* Gruppen sind in Tabelle "RZLLITAB" abgelegt, grouptype = 'S' (Server)
* Transaktion RZ12
CONSTANTS: co_default_task_group TYPE rzllitab-classname VALUE 'parallel_generators'.
* Factory-Funktion
CLASS-METHODS:
get_instance
IMPORTING
i_task_group TYPE rzllitab-classname DEFAULT co_default_task_group
RETURNING VALUE(rv_ref_task_manager) TYPE REF TO lcl_task_manager.
* Startet die parallele Abarbeitung der Eingabedaten
METHODS:
start_parallel_working
IMPORTING
i_it_input_data TYPE lcl_task=>ty_it_input_data
i_wait_time_sec TYPE i DEFAULT 10
i_max_retries TYPE i DEFAULT 3
EXPORTING
e_it_final_result TYPE ty_it_final_result
e_it_statistic TYPE ty_it_statistic
e_tasks_started TYPE i
e_tasks_ended TYPE i
e_tasks_missed TYPE i
RETURNING VALUE(rv_work_complete) TYPE boolean.
METHODS:
get_max_tasks
RETURNING VALUE(rv_max_tasks) TYPE i.
METHODS:
get_free_tasks
RETURNING VALUE(rv_free_tasks) TYPE i.
PRIVATE SECTION.
TYPES: BEGIN OF ty_task,
task_number TYPE i,
o_task TYPE REF TO lcl_task,
END OF ty_task.
* Null-Referenz
CONSTANTS: null TYPE REF TO lcl_task_manager VALUE IS INITIAL.
* Referenz auf ein Task-Objekt
CLASS-DATA: o_task_manager TYPE REF TO lcl_task_manager.
CLASS-DATA: lv_max_tasks TYPE i.
CLASS-DATA: lv_free_tasks TYPE i.
* Zähler
CLASS-DATA: lv_tasks_started TYPE i.
CLASS-DATA: lv_tasks_ended TYPE i.
CLASS-DATA: lv_active_tasks TYPE i.
* globale Taskliste
CLASS-DATA: it_tasks TYPE SORTED TABLE OF ty_task WITH UNIQUE KEY task_number.
* Ergebnisliste
CLASS-DATA: it_final_result TYPE ty_it_final_result.
* Ergebnisliste
CLASS-DATA: it_statistic TYPE ty_it_statistic.
* Handler für data_received-Ereignis der Tasks
METHODS:
on_task_completed FOR EVENT data_received OF lcl_task
IMPORTING
e_subrc
e_runtime
e_retries
e_input_data
e_output_data
sender.
* Hilfsfunktionen
METHODS:
get_free_task
RETURNING VALUE(rv_task_number) TYPE i.
METHODS:
get_task
IMPORTING
i_task_number TYPE i
RETURNING VALUE(rv_ref_task) TYPE REF TO lcl_task.
METHODS:
start_task
IMPORTING
i_input_data TYPE lcl_task=>ty_input_data
i_wait_time_sec TYPE i DEFAULT 10
i_max_retries TYPE i DEFAULT 3
RETURNING VALUE(rv_ok) TYPE boolean.
ENDCLASS.
*--------------------------------------------------------------------*
CLASS lcl_task_manager IMPLEMENTATION.
*--------------------------------------------------------------------*
* Factory Methode für Singleton (genau eine Referenz) auf den
* Taskmanager zurück
*--------------------------------------------------------------------*
* -> i_task_group - Name der RFC-Task-Group, in der die Tasks ablaufen
* sollen
* Vorgabewert ist 'parallel_generators', welche
* standardmäßig von Admins eingerichtet wird
* Gruppen sind in Tabelle "RZLLITAB" abgelegt,
* grouptype = 'S' (Server)
* Transaktion RZ12
* <- rv_ref_task_manager - Referenz auf Task-Manager-Objekt
*--------------------------------------------------------------------*
METHOD get_instance.
rv_ref_task_manager = null.
IF o_task_manager IS INITIAL.
DATA(lv_task_group) = i_task_group.
lv_max_tasks = 0.
lv_free_tasks = 0.
IF lv_task_group IS INITIAL.
* falls keine task group übergeben wurde, eine heraussuchen
* Verfügbare RFC-Gruppen für Parallelverarbeitung: Transaktion RZ12
SELECT SINGLE classname FROM rzllitab INTO @lv_task_group WHERE grouptype = 'S'.
ENDIF.
IF NOT lv_task_group IS INITIAL.
* Start neue LUW
COMMIT WORK.
* Parallel Background Tasks - Initialisierung der PBT-Umgebung
* Sollte nur 1x aufgerufen werden, sonst Exception "pbt_env_already_initialized"
CALL FUNCTION 'SPBT_INITIALIZE'
EXPORTING
group_name = lv_task_group
IMPORTING
max_pbt_wps = lv_max_tasks
free_pbt_wps = lv_free_tasks
EXCEPTIONS
invalid_group_name = 1
internal_error = 2
pbt_env_already_initialized = 3
currently_no_resources_avail = 4
no_pbt_resources_found = 5
cant_init_different_pbt_groups = 6
OTHERS = 7.
* wenn Initialisierung ok
IF sy-subrc = 0.
* wenn genug freie Tasks vorhanden
IF lv_free_tasks >= 1.
o_task_manager = NEW #( ).
lv_tasks_started = 0.
lv_tasks_ended = 0.
lv_active_tasks = 0.
CLEAR: it_tasks.
* Anzahl freie Tasks zur Liste hinzufügen
DO lv_free_tasks TIMES.
DATA(o_task) = NEW lcl_task( i_task_number = sy-index
i_task_group = lv_task_group ).
* Eventhandler für beendete Tasks
SET HANDLER o_task_manager->on_task_completed FOR o_task.
APPEND VALUE #( task_number = sy-index
o_task = o_task ) TO it_tasks.
ENDDO.
rv_ref_task_manager = o_task_manager.
ENDIF.
ENDIF.
ENDIF.
ELSE.
rv_ref_task_manager = o_task_manager.
ENDIF.
ENDMETHOD.
*--------------------------------------------------------------------*
* Parallele / asynchrone Abarbeitung anschieben
*--------------------------------------------------------------------*
* -> i_it_input_data - Eingabedaten
* -> i_wait_time_sec - Wartezeit für Abarbeitung aller Tasks in sec
* -> i_max_retries - max. Anz. Versuche
* <- e_it_final_result - Erbebnismenge der parallelen Verarbeitung
* <- e_tasks_started - Anz. gestartete Tasks
* <- e_tasks_ended - Anz. beendete Tasks (sollte im Idealfall
* e_tasks_started entsprechen
* <- e_tasks_missed - Anz. Tasks, die nicht gestartet werden
* konnten
*--------------------------------------------------------------------*
METHOD start_parallel_working.
rv_work_complete = abap_false.
lv_tasks_started = 0.
lv_tasks_ended = 0.
lv_active_tasks = 0.
CLEAR: it_final_result.
CLEAR: it_statistic.
DATA(lv_tasks_missed) = 0.
* Übergebene Workitems abarbeiten
LOOP AT i_it_input_data ASSIGNING FIELD-SYMBOL(<fs_input_data>).
* pro Workitem einen Task (asynchron) starten
IF me->start_task( i_input_data = <fs_input_data>
i_max_retries = i_max_retries ).
ELSE.
* Fehler: kein Task für Workitem verfügbar
lv_tasks_missed = lv_tasks_missed + 1.
APPEND VALUE #( task_number = -1
runtime = 0
retries = i_max_retries
input_data = <fs_input_data>
output_part_size = 0
subrc = -1
successful = abap_false ) TO it_statistic.
ENDIF.
ENDLOOP.
* warten bis alle Tasks fertig sind
WAIT FOR ASYNCHRONOUS TASKS UNTIL ( lv_active_tasks = 0 ) UP TO i_wait_time_sec SECONDS.
IF sy-subrc = 0.
rv_work_complete = abap_true.
ENDIF.
* Ergebnisrückgabe
e_it_final_result = it_final_result.
e_it_statistic = it_statistic.
e_tasks_started = lv_tasks_started.
e_tasks_ended = lv_tasks_ended.
e_tasks_missed = lv_tasks_missed.
ENDMETHOD.
*--------------------------------------------------------------------*
* Gibt Anzahl max. Tasks
*--------------------------------------------------------------------*
* <- rv_max_tasks - Anzahl max. Tasks
*--------------------------------------------------------------------*
METHOD get_max_tasks.
rv_max_tasks = lv_max_tasks.
ENDMETHOD.
*--------------------------------------------------------------------*
* Gibt Anzahl freie Tasks
*--------------------------------------------------------------------*
* <- rv_free_tasks - Anzahl freie Tasks
*--------------------------------------------------------------------*
METHOD get_free_tasks.
rv_free_tasks = lv_free_tasks.
ENDMETHOD.
*--------------------------------------------------------------------*
* Einen Task starten
*--------------------------------------------------------------------*
* -> i_input_data - Eingabedaten für die Verarbeitung
* -> i_wait_time_sec - Wartezeit für Abarbeitung aller Tasks in sec
* -> i_max_retries - max. Anz. Versuche wenn System "busy" bis
* - Abbruch erfolgt
* <- rv_ok - true, wenn Task gestartet werden konnte
*--------------------------------------------------------------------*
METHOD start_task.
rv_ok = abap_false.
DATA(lv_retries) = 0.
* n Neuversuche einen freien Task zu bekommen
WHILE lv_retries < i_max_retries.
DATA(lv_tasknumber) = me->get_free_task( ).
IF lv_tasknumber <> -1.
DATA(o_task) = me->get_task( lv_tasknumber ).
IF o_task IS BOUND.
DATA(lv_subrc) = o_task->start_async_call( i_input_data = i_input_data
i_started_after_n_retries = lv_retries ).
CASE lv_subrc.
WHEN 0.
* Ok
lv_tasks_started = lv_tasks_started + 1.
lv_active_tasks = lv_active_tasks + 1.
lv_retries = i_max_retries.
rv_ok = abap_true.
WHEN 1 OR 2.
* Failure
lv_retries = i_max_retries.
WHEN 3.
* Busy
IF lv_tasks_started > 0.
WAIT FOR ASYNCHRONOUS TASKS UNTIL ( lv_tasks_ended >= lv_tasks_started ) UP TO i_wait_time_sec SECONDS.
ENDIF.
IF sy-subrc = 0.
lv_retries = lv_retries + 1.
ELSE.
lv_retries = i_max_retries.
ENDIF.
WHEN OTHERS.
* Sollte nicht auftreten
lv_retries = i_max_retries.
ENDCASE.
ELSE.
* Task not found (sollte nicht vorkommen)
lv_retries = lv_retries + 1.
ENDIF.
ELSE.
* kein freier Task verfügbar -> warten
WAIT FOR ASYNCHRONOUS TASKS UNTIL ( lv_tasks_ended >= lv_tasks_started ) UP TO i_wait_time_sec SECONDS.
IF sy-subrc = 0.
lv_retries = lv_retries + 1.
ELSE.
lv_retries = i_max_retries.
ENDIF.
ENDIF.
ENDWHILE.
ENDMETHOD.
*--------------------------------------------------------------------*
* Freien Task aus der Liste der verfügbaren suchen
*--------------------------------------------------------------------*
* <- rv_task_number - Tasknummer des gefundenen Tasks
*--------------------------------------------------------------------*
METHOD get_free_task.
rv_task_number = -1.
LOOP AT it_tasks ASSIGNING FIELD-SYMBOL(<fs_task>).
IF <fs_task>-o_task->is_running( ) = abap_false.
rv_task_number = <fs_task>-task_number.
EXIT.
ENDIF.
ENDLOOP.
ENDMETHOD.
*--------------------------------------------------------------------*
* Referenz auf Task anhand der Tasknummer holen
*--------------------------------------------------------------------*
* -> i_task_number - Tasknummer
* <- rv_ref_task - Referenz auf Task
*--------------------------------------------------------------------*
METHOD get_task.
IF line_exists( it_tasks[ task_number = i_task_number ] ).
ASSIGN it_tasks[ task_number = i_task_number ] TO FIELD-SYMBOL(<fs_task>).
IF <fs_task> IS ASSIGNED.
rv_ref_task = <fs_task>-o_task.
ENDIF.
ENDIF.
ENDMETHOD.
*--------------------------------------------------------------------*
* Eventhandler, wenn Task ferig
*--------------------------------------------------------------------*
* -> e_input_data - Eingabedaten des Tasks
* -> e_output_data - Ausgabedaten des Tasks
* -> sender - Referenz auf Task
*--------------------------------------------------------------------*
METHOD on_task_completed.
* Zähler setzen
lv_tasks_ended = lv_tasks_ended + 1.
lv_active_tasks = lv_active_tasks - 1.
APPEND VALUE #( task_number = sender->get_task_number( )
runtime = e_runtime
retries = e_retries
input_data = e_input_data
output_part_size = lines( e_output_data )
subrc = e_subrc
successful = abap_true ) TO it_statistic.
* Hier Daten an Ergebnistabelle einfach anfügen
LOOP AT e_output_data ASSIGNING FIELD-SYMBOL(<fs_output_data>).
APPEND VALUE #( input_data = e_input_data
output_data = <fs_output_data> ) TO it_final_result.
ENDLOOP.
ENDMETHOD.
ENDCLASS.