前言
在 SAP ABAP 中,实现多线程的方式有多种,每种方式的工作原理、适用场景和复杂度各不相同,以下是几种常见的多线程实现方式,理论上BACKGROUND TASK或者后台JOB也算是实现多线程的一种方式,这里只总结一下四种常见的且效率提升比较大的方式。
- 基于CALL FUNCTION STARTING NEW TASK
- 基于SPTA框架
- 基于CL_ABAP_PARALLEL
- 基于IF_ABAP_PARALLER接口
为什么需要多线程/并行处理
并行处理主要是为了提高任何 ABAP 程序的性能。 使用并行处理框架,我们可以显著缩短任何程序的处理时间,尤其是在数据量非常大的情况下。 并行处理框架背后的基本概念是将大量的数据分成几个小的工作包,并将不同的工作包处理成不同的任务 。因此每个工作流程将同时并行处理,这将显着减少时间。实际开发过程中经常会遇到数据量太大导致程序效率变慢的情况,这时我们就可以结合实际情况和系统版本来选择具体的并行实现方式。但是需要注意的是,并行处理不适合需要严格按照顺序执行的业务场景。
CALL FUNCTION STARTING NEW TASK
是最早也是最常规的多线程实现方式,使用异步RFC创建后台任务,主程序不会等待任务完成。
优势:简单易用,适合轻量级的异步任务,并且支持跨系统调用。
劣势:需要手动管理任务同步和错误处理,不适合复杂场景,任务量过多可能耗尽系统资源。
效率提升程度:中等。
开发复杂度:低。
版本:SAP ECC 6.0可用
SPTA框架
SPTA框架是SAP提供的最成熟的并行处理系统,其核心函数为SPTA_PARA_PROCESS_START_2,传统的并行处理方式相对来说比较麻烦,并且可能存在一些内存问题,但是SPTA框架中,不用担心这一点,所有与ABAP内存相关的问题都有安全处理,所以大多数情况下,使用SPTA处理并行任务是比较好的选择。
优势:提供任务管理框架,简化任务分配和监控,支持动态调整任务数量,适合大规模并行任务。
劣势:配置和使用相对复杂一点,需要理解框架的工作原理,需要手工进行内容转换。
效率提升程度:高。
开发复杂程度:中高。
版本:SAP ECC 6.0可用
CL_ABAP_PARALLEL
SAP 提供的一个类,用于简化并行处理的实现。它基于 ABAP 的 RFC(Remote Function Call)机制,通过创建多个并行会话(子任务)来分配和处理任务。
优势:开发人员可以快速实现并行处理,相较于前两种更容易一些。
劣势:配置和使用相对复杂一点,需要理解框架的工作原理,仍然需要手工进行内容转换。
效率提升程度:高。
开发复杂程度:中。
版本:SAP NetWeaver 7.52可用。
IF_ABAP_PARALLEL
SAP提供的一个标准接口,相比较于CL_ABAP_PARALLEL需要关注的内容会简单一些,其原理和CL_ABAP_PARALLEL类似,都是需要拆分数据传递给框架,由框架自己完成并行处理。
优势:开发人员可以快速实现并行处理,无需数据打包、解包的处理。
劣势:没有太多的文档说明,需要理解框架的工作原理才可以进行,并且需要较高的系统版本。
效率提升程度:高。
开发复杂程序:中。
版本:SAP NetWeaver 7.54可用。
示例说明:CALL FUNCTION STARTING NEW TASK
后续每种测试场景都是同样的步骤:
1.准备测试数据
2.顺序调用,loop依次执行。
3.并行处理,不同方式的实现代码不同。
4.记录时间差异并输出。
5.模拟业务执行的函数 ZPARA_SLEEP_TEST只有一行WAIT语句。
并行处理核心代码:
gt_request中存放了我需要处理的所有数据,为了测试,我将每一条数据都做并行处理,实际开发过程中可以按照自己的需求分组打包处理,比如按同一订单,或者同一物料等等。
lv_task_name为每个任务指定一个名称,最好不要重复,有助于追溯问题。
DESTINATION:需要调用外部系统时,请指定具体的目标连接名,未指定时隐式使用'NONE'目标。
IN GROUP:需要指定具体的服务器实例时可以使用该参数指定,这里没特殊要求默认 DEFAULT即可。
要调用的函数必须是可以远程调用的模块:
PERFORMING call_back_form ON END OF TASK:指定函数结束时的回调例程,用于接收函数的执行结果。
EXPORTING:只能使用导出参数,其他类型的参数只能在回调函数中使用RECEIVE语法接收。
WAIT UNTIL:判断所有的任务处理完之后进行后续操作。
示例说明:SPTA(调用函数 SPTA_PARA_PROCESS_START_2)
并行处理核心代码:
核心参数:
- BEFORE_RFC_CALLBACK_FORM:在调用 RFC 函数之前,函数会调用此例程,这个例程中我们需要按照具体需求将数据拆分成一个一个的小的package交给框架去分发处理。
- IN_RFC_CALLBACK_FORM:创建好的每个package数据都会调用此例程,在这里例程里,我们就可以执行我们具体想并行处理的逻辑,比如单据创建,订单过账等等。
- AFTER_RFC_CALLBACK_FORM:收集处理完的数据和消息内容。
基于上面这三个关键的例程,即可实现SPTA框架的并行处理。
示例说明:CL_ABAP_PARALLEL
该框架的调用过程如下:定义并行对象->打包处理数据->调用RUN方法(框架会自动调用do方法,do方法中包含实际业务处理的具体逻辑,即我们需要重写的方法)->解包处理结果->收集所有处理结果。
并行处理核心代码:
首先我们需要创建一个本地类,也可以SE24创建,需要继承 cl_abap_parallel,并重写do方法。
do方法中,我们需要先对数据进行解包,然后按照实际需求进行处理,处理完成之后再次打包,具体参数我们可以在SE24中进行查看。
重写好do方法之后,我们就按照上面说的处理顺序来执行即可:
创建并行对象时,可以使用P_PERCENTAGE来指定可用进程的百分之(1~100),也可以使用参数P_NUM_TASKS直接指定可用进程的数量。
示例说明:IF_ABAP_PARALLEL
该接口的调用过程如下:定义并行对象->调用RUN_INST方法(框架会自动调用do方法,do方法中包含实际业务处理的具体逻辑,即我们需要重写的方法)->收集所有处理结果。
跟CL_ABAP_PARALLEL不同,虽然看起来我们不需要打包解包数据,但实际上我们还需要做一点额外的事情,才能正常接收处理数据。
并行处理核心代码:
首先创建本地类,指定接口 if_abap_parallel,并创建构造方法和一个返回方法,以及两个属性字段。
实现具体的方法:
- 构造方法:用于在实例化对象时,将数据保存到自身属性字段中;
- do方法:用于处理实际的业务逻辑;
- get_response方法:用于后续收集处理结果;
完成以上内容后,按照上面的处理顺序来调用即可:
完整测试代码:
基于STARTING NEW TASK
*&---------------------------------------------------------------------*
*& Report ZPARALLEL_DEMO1
*&---------------------------------------------------------------------*
*& Description: 基于STARTING NEW TASK的多线程测试demo
*& Author: DeveloperMrMeng
*& Date: 2025.03.03
*&---------------------------------------------------------------------*
REPORT zparallel_demo1.
DATA:
gt_request TYPE bapiret2_t,
gt_response_seq TYPE bapiret2_t,
gt_response_para TYPE bapiret2_t,
gv_diff_seconds_seq TYPE timestampl,
gv_diff_seconds_para TYPE timestampl.
START-OF-SELECTION.
PERFORM ready_test_data.
PERFORM sequence_process.
PERFORM parallel_process.
cl_demo_output=>new( )->write( |Diffrence Seconds: { gv_diff_seconds_seq }|
)->write( gt_response_seq
)->write( |Diffrence Seconds: { gv_diff_seconds_para }|
)->write( gt_response_para
)->display( ).
*&---------------------------------------------------------------------*
*& Form ready_test_data
*&---------------------------------------------------------------------*
*& 准备测试数据
*&---------------------------------------------------------------------*
FORM ready_test_data .
DATA:
ls_request TYPE bapiret2,
lv_index TYPE sy-index.
DO 5 TIMES.
lv_index = sy-index.
DO lv_index TIMES.
ls_request-number = lv_index.
APPEND ls_request TO gt_request.
ENDDO.
ENDDO.
ENDFORM.
*&---------------------------------------------------------------------*
*& Form sequence_process
*&---------------------------------------------------------------------*
*& 顺序执行
*&---------------------------------------------------------------------*
FORM sequence_process .
DATA lt_response TYPE bapiret2_t.
GET TIME STAMP FIELD DATA(lv_timestamp_begin).
LOOP AT gt_request INTO DATA(ls_request).
* 模拟数据库提交操作
CALL FUNCTION 'ZPARA_SLEEP_TEST'
EXPORTING
wait_time = 1
IMPORTING
status = ls_request-type
EXCEPTIONS
internal_error = 1
OTHERS = 2.
APPEND ls_request TO lt_response.
ENDLOOP.
gt_response_seq = lt_response.
GET TIME STAMP FIELD DATA(lv_timestamp_end).
gv_diff_seconds_seq = cl_abap_tstmp=>subtract( tstmp1 = lv_timestamp_end
tstmp2 = lv_timestamp_begin ).
ENDFORM.
*&---------------------------------------------------------------------*
*& Form parallel_process
*&---------------------------------------------------------------------*
*& 多线程执行
*&---------------------------------------------------------------------*
FORM parallel_process .
DATA:
lt_response TYPE bapiret2_t,
lv_task_name TYPE string,
lv_snd_jobs TYPE i,
lv_rcv_jobs TYPE i,
lv_exc_flag TYPE i,
lv_message TYPE char80,
lv_wait_time TYPE int2.
GET TIME STAMP FIELD DATA(lv_timestamp_begin).
LOOP AT gt_request ASSIGNING FIELD-SYMBOL(<ls_request>).
lv_task_name = 'Task' && sy-tabix.
<ls_request>-parameter = lv_task_name.
IF lv_task_name = 'Task8'.
lv_wait_time = 2.
ELSE.
lv_wait_time = 1.
ENDIF.
* 模拟数据库提交操作
CALL FUNCTION 'ZPARA_SLEEP_TEST'
STARTING NEW TASK lv_task_name
DESTINATION IN GROUP DEFAULT
PERFORMING call_back_form ON END OF TASK
EXPORTING
wait_time = lv_wait_time
EXCEPTIONS
system_failure = 1 MESSAGE lv_message
communication_failure = 2 MESSAGE lv_message
resource_failure = 3.
CASE sy-subrc.
WHEN 0.
lv_snd_jobs += 1.
WHEN 1 OR 2.
WRITE lv_message.
WHEN 3.
IF lv_snd_jobs >= 1 AND lv_exc_flag = 0.
lv_exc_flag = 1.
WAIT UNTIL lv_rcv_jobs >= lv_snd_jobs UP TO 5 SECONDS.
ENDIF.
IF sy-subrc = 0.
lv_exc_flag = 0.
ELSE.
WRITE 'Resource failure'.
ENDIF.
WHEN OTHERS.
WRITE 'Other error'.
ENDCASE.
ENDLOOP.
WAIT UNTIL lv_rcv_jobs >= lv_snd_jobs.
GET TIME STAMP FIELD DATA(lv_timestamp_end).
gv_diff_seconds_para = cl_abap_tstmp=>subtract( tstmp1 = lv_timestamp_end
tstmp2 = lv_timestamp_begin ).
ENDFORM.
*&---------------------------------------------------------------------*
*& Form call_back_form
*&---------------------------------------------------------------------*
*& 回调函数
*&---------------------------------------------------------------------*
FORM call_back_form USING uv_taskname.
DATA:
ls_reponse TYPE bapiret2.
RECEIVE RESULTS FROM FUNCTION 'ZPARA_SLEEP_TEST'
IMPORTING
status = ls_reponse-type
EXCEPTIONS
internal_error = 1.
ls_reponse-parameter = uv_taskname.
APPEND ls_reponse TO gt_response_para.
CLEAR ls_reponse.
ENDFORM.
基于SPTA框架
*&---------------------------------------------------------------------*
*& Report ZPARALLEL_DEMO2
*&---------------------------------------------------------------------*
*& Description: 基于SPTA框架的多线程测试demo
*& Author: DeveloperMrMeng
*& Date: 2025.03.03
*&---------------------------------------------------------------------*
REPORT zparallel_demo2.
DATA:
gt_request TYPE bapiret2_t,
gt_response_seq TYPE bapiret2_t,
gt_response_para TYPE bapiret2_t,
gv_diff_seconds_seq TYPE timestampl,
gv_diff_seconds_para TYPE timestampl.
START-OF-SELECTION.
PERFORM ready_test_data.
PERFORM sequence_process.
PERFORM parallel_process.
cl_demo_output=>new( )->write( |Diffrence Seconds: { gv_diff_seconds_seq }|
)->write( gt_response_seq
)->write( |Diffrence Seconds: { gv_diff_seconds_para }|
)->write( gt_response_para
)->display( ).
*&---------------------------------------------------------------------*
*& Form ready_test_data
*&---------------------------------------------------------------------*
*& 准备测试数据
*&---------------------------------------------------------------------*
FORM ready_test_data .
DATA:
ls_request TYPE bapiret2,
lv_index TYPE sy-index.
DO 5 TIMES.
lv_index = sy-index.
DO lv_index TIMES.
ls_request-number = lv_index.
APPEND ls_request TO gt_request.
ENDDO.
ENDDO.
ENDFORM.
*&---------------------------------------------------------------------*
*& Form sequence_process
*&---------------------------------------------------------------------*
*& 顺序执行
*&---------------------------------------------------------------------*
FORM sequence_process .
DATA lt_response TYPE bapiret2_t.
GET TIME STAMP FIELD DATA(lv_timestamp_begin).
LOOP AT gt_request INTO DATA(ls_request).
* 模拟数据库提交操作
CALL FUNCTION 'ZPARA_SLEEP_TEST'
EXPORTING
wait_time = 1
IMPORTING
status = ls_request-type
EXCEPTIONS
internal_error = 1
OTHERS = 2.
APPEND ls_request TO lt_response.
ENDLOOP.
gt_response_seq = lt_response.
GET TIME STAMP FIELD DATA(lv_timestamp_end).
gv_diff_seconds_seq = cl_abap_tstmp=>subtract( tstmp1 = lv_timestamp_end
tstmp2 = lv_timestamp_begin ).
ENDFORM.
*&---------------------------------------------------------------------*
*& Form parallel_process
*&---------------------------------------------------------------------*
*& 多线程执行
*&---------------------------------------------------------------------*
FORM parallel_process .
DATA:
lt_response TYPE bapiret2_t,
lv_task_name TYPE string,
lv_snd_jobs TYPE i,
lv_rcv_jobs TYPE i,
lv_exc_flag TYPE i,
lv_message TYPE char80,
lv_wait_time TYPE int2.
DATA:
lv_sergroup TYPE spta_rfcgr,
lv_rfcgroup TYPE rzlli_apcl.
CALL 'C_SAPGPARAM' "#EC CI_CCALL
ID 'NAME' FIELD 'rdisp/myname'
ID 'VALUE' FIELD lv_sergroup.
SELECT SINGLE classname
INTO lv_rfcgroup
FROM rzllitab
WHERE grouptype = 'S'
AND applserver = lv_sergroup.
IF sy-subrc <> 0.
MESSAGE 'Server Group Read Error. Contact Administrator.' TYPE 'E'.
ENDIF.
GET TIME STAMP FIELD DATA(lv_timestamp_begin).
CALL FUNCTION 'SPTA_PARA_PROCESS_START_2'
EXPORTING
server_group = lv_rfcgroup
before_rfc_callback_form = 'BEFORE_RFC_CALLBACK_FORM'
in_rfc_callback_form = 'IN_RFC_CALLBACK_FORM'
after_rfc_callback_form = 'AFTER_RFC_CALLBACK_FORM'
callback_prog = sy-repid
CHANGING
user_param = gt_request
EXCEPTIONS
invalid_server_group = 1
no_resources_available = 2
OTHERS = 3.
GET TIME STAMP FIELD DATA(lv_timestamp_end).
gv_diff_seconds_para = cl_abap_tstmp=>subtract( tstmp1 = lv_timestamp_end
tstmp2 = lv_timestamp_begin ).
ENDFORM.
*&---------------------------------------------------------------------*
*& Form parallel_process
*&---------------------------------------------------------------------*
FORM before_rfc_callback_form
USING
us_before_rfc_imp TYPE spta_t_before_rfc_imp
CHANGING
cs_before_rfc_exp TYPE spta_t_before_rfc_exp
ct_rfcdata TYPE spta_t_indxtab
ct_failed_objects TYPE spta_t_failed_objects
ct_objects_in_task TYPE spta_t_pending_objects
ct_user_param TYPE bapiret2_t.
DATA:
lv_package_size TYPE i,
lv_counter TYPE i,
lt_request TYPE bapiret2_t.
lv_package_size = 1.
* 按实际需求进行分批,比如同一订单号为一批数据,此处为了测试按照每条数据都并行处理
LOOP AT ct_user_param INTO DATA(ls_user_param).
IF lv_counter < lv_package_size.
APPEND ls_user_param TO lt_request.
DELETE ct_user_param INDEX 1.
lv_counter += 1.
ELSE.
EXIT.
ENDIF.
ENDLOOP.
* 将数据转换为SPTA框架所需要的类型
CALL FUNCTION 'SPTA_INDX_PACKAGE_ENCODE'
EXPORTING
data = lt_request
IMPORTING
indxtab = ct_rfcdata.
IF lt_request IS NOT INITIAL.
cs_before_rfc_exp-start_rfc = 'X'.
ELSE.
cs_before_rfc_exp-start_rfc = ''.
ENDIF.
ENDFORM.
*&---------------------------------------------------------------------*
*& Form in_rfc_callback_form
*&---------------------------------------------------------------------*
FORM in_rfc_callback_form
USING
us_in_rfc_imp TYPE spta_t_in_rfc_imp
CHANGING
cs_in_rfc_exp TYPE spta_t_in_rfc_exp
ct_rfcdata TYPE spta_t_indxtab.
DATA:
lt_request TYPE bapiret2_t,
lv_wait_time TYPE int2.
* 将数据转换为实际处理所需要的可读类型
CALL FUNCTION 'SPTA_INDX_PACKAGE_DECODE'
EXPORTING
indxtab = ct_rfcdata
IMPORTING
data = lt_request.
* 模拟数据库提交操作
LOOP AT lt_request ASSIGNING FIELD-SYMBOL(<ls_request>).
* 模拟某条数据处理较慢
IF <ls_request>-number = 1.
lv_wait_time = 2.
ELSE.
lv_wait_time = 1.
ENDIF.
* 模拟业务数据执行
CALL FUNCTION 'ZPARA_SLEEP_TEST'
EXPORTING
wait_time = lv_wait_time
IMPORTING
status = <ls_request>-type
EXCEPTIONS
internal_error = 1
OTHERS = 2.
ENDLOOP.
* 将数据转换为SPTA框架所需要的类型
CALL FUNCTION 'SPTA_INDX_PACKAGE_ENCODE'
EXPORTING
data = lt_request
IMPORTING
indxtab = ct_rfcdata.
ENDFORM.
*&---------------------------------------------------------------------*
*& Form after_rfc_callback_form
*&---------------------------------------------------------------------*
FORM after_rfc_callback_form
USING
ut_rfcdata TYPE spta_t_indxtab
uv_rfcsubrc TYPE sy-subrc
uv_rfcmsg TYPE spta_t_rfcmsg
ut_objects_in_task TYPE spta_t_pending_objects
us_after_rfc_imp TYPE spta_t_after_rfc_imp
CHANGING
cs_after_rfc_exp TYPE spta_t_after_rfc_exp
ct_user_param TYPE bapiret2_t.
DATA:
lt_request TYPE bapiret2_t.
* 将数据转换为实际处理所需要的可读类型
CALL FUNCTION 'SPTA_INDX_PACKAGE_DECODE'
EXPORTING
indxtab = ut_rfcdata
IMPORTING
data = lt_request.
APPEND LINES OF lt_request TO gt_response_para.
ENDFORM.
基于CL_ABAP_PARALLEL
*&---------------------------------------------------------------------*
*& Report ZPARALLEL_DEMO3
*&---------------------------------------------------------------------*
*& Description: 基于CL_ABAP_PARALLEL类的多线程测试demo
*& Author: DeveloperMrMeng
*& Date: 2025.03.03
*&---------------------------------------------------------------------*
REPORT zparallel_demo3.
*&---------------------------------------------------------------------*
*& CLASS zcl_abap_parallel definition
*&---------------------------------------------------------------------*
CLASS zcl_abap_parallel DEFINITION
INHERITING FROM cl_abap_parallel FINAL
CREATE PUBLIC.
PUBLIC SECTION.
METHODS do REDEFINITION.
ENDCLASS.
DATA:
gt_request TYPE bapiret2_t,
gt_response_seq TYPE bapiret2_t,
gt_response_para TYPE bapiret2_t,
gv_diff_seconds_seq TYPE timestampl,
gv_diff_seconds_para TYPE timestampl.
START-OF-SELECTION.
PERFORM ready_test_data.
PERFORM sequence_process.
PERFORM parallel_process.
cl_demo_output=>new( )->write( |Diffrence Seconds: { gv_diff_seconds_seq }|
)->write( gt_response_seq
)->write( |Diffrence Seconds: { gv_diff_seconds_para }|
)->write( gt_response_para
)->display( ).
*&---------------------------------------------------------------------*
*& Form ready_test_data
*&---------------------------------------------------------------------*
*& 准备测试数据
*&---------------------------------------------------------------------*
FORM ready_test_data .
DATA:
ls_request TYPE bapiret2,
lv_index TYPE sy-index.
DO 5 TIMES.
lv_index = sy-index.
DO lv_index TIMES.
ls_request-number = lv_index.
APPEND ls_request TO gt_request.
ENDDO.
ENDDO.
ENDFORM.
*&---------------------------------------------------------------------*
*& Form sequence_process
*&---------------------------------------------------------------------*
*& 顺序执行
*&---------------------------------------------------------------------*
FORM sequence_process .
DATA lt_response TYPE bapiret2_t.
GET TIME STAMP FIELD DATA(lv_timestamp_begin).
LOOP AT gt_request INTO DATA(ls_request).
* 模拟数据库提交操作
CALL FUNCTION 'ZPARA_SLEEP_TEST'
EXPORTING
wait_time = 1
IMPORTING
status = ls_request-type
EXCEPTIONS
internal_error = 1
OTHERS = 2.
APPEND ls_request TO lt_response.
ENDLOOP.
gt_response_seq = lt_response.
GET TIME STAMP FIELD DATA(lv_timestamp_end).
gv_diff_seconds_seq = cl_abap_tstmp=>subtract( tstmp1 = lv_timestamp_end
tstmp2 = lv_timestamp_begin ).
ENDFORM.
*&---------------------------------------------------------------------*
*& Form parallel_process
*&---------------------------------------------------------------------*
*& 多线程执行
*&---------------------------------------------------------------------*
FORM parallel_process .
DATA:
lt_in_xstring TYPE xstring,
lt_in TYPE cl_abap_parallel=>t_in_tab,
lt_out TYPE cl_abap_parallel=>t_out_tab,
lt_response TYPE bapiret2_t,
lt_request TYPE bapiret2_t.
GET TIME STAMP FIELD DATA(lv_timestamp_begin).
* 按实际需求进行分批,比如同一订单号为一批数据,此处为了测试按照每条数据都并行处理
LOOP AT gt_request INTO DATA(ls_request).
APPEND ls_request TO lt_request.
* 将数据转换为接口所需要的类型
CALL TRANSFORMATION id SOURCE in = lt_request
RESULT XML lt_in_xstring.
INSERT lt_in_xstring INTO TABLE lt_in.
CLEAR:
lt_request,
lt_in_xstring.
ENDLOOP.
* p_percentage参数用来指定可用进程百分比,可以指定为0~100
DATA(lo_parallel) = NEW zcl_abap_parallel( p_percentage = 95 ).
* 多线程启动
lo_parallel->run( EXPORTING p_in_tab = lt_in
IMPORTING p_out_tab = lt_out ).
* 解析执行结果
LOOP AT lt_out INTO DATA(ls_out).
* 将数据转换为实际处理所需要的可读类型
CALL TRANSFORMATION id SOURCE XML ls_out-result
RESULT out = lt_response.
INSERT LINES OF lt_response INTO TABLE gt_response_para.
CLEAR lt_response.
ENDLOOP.
GET TIME STAMP FIELD DATA(lv_timestamp_end).
gv_diff_seconds_para = cl_abap_tstmp=>subtract( tstmp1 = lv_timestamp_end
tstmp2 = lv_timestamp_begin ).
ENDFORM.
*&---------------------------------------------------------------------*
*& CLASS zcl_abap_parallel implementation
*&---------------------------------------------------------------------*
CLASS zcl_abap_parallel IMPLEMENTATION.
METHOD do.
DATA:
lt_request TYPE bapiret2_t,
lv_wait_time TYPE int2.
* 将数据转换为实际处理所需要的可读类型
CALL TRANSFORMATION id SOURCE XML p_in
RESULT in = lt_request.
LOOP AT lt_request ASSIGNING FIELD-SYMBOL(<ls_request>).
* 模拟某条数据处理较慢
IF <ls_request>-number = 1.
lv_wait_time = 2.
ELSE.
lv_wait_time = 1.
ENDIF.
* 模拟业务数据执行
CALL FUNCTION 'ZPARA_SLEEP_TEST'
EXPORTING
wait_time = lv_wait_time
IMPORTING
status = <ls_request>-type
EXCEPTIONS
internal_error = 1
OTHERS = 2.
ENDLOOP.
* 将数据转换为接口所需要的类型
CALL TRANSFORMATION id SOURCE out = lt_request
RESULT XML p_out.
ENDMETHOD.
ENDCLASS.
基于IF_ABAP_PARALLEL
*&---------------------------------------------------------------------*
*& Report ZPARALLEL_DEMO4
*&---------------------------------------------------------------------*
*& Description: 基于IF_ABAP_PARALLEL接口的多线程测试demo
*& Author: DeveloperMrMeng
*& Date: 2025.03.03
*&---------------------------------------------------------------------*
REPORT zparallel_demo4.
*&---------------------------------------------------------------------*
*& CLASS zcl_abap_parallel definition
*&---------------------------------------------------------------------*
CLASS zcl_abap_parallel DEFINITION.
PUBLIC SECTION.
INTERFACES if_abap_parallel .
INTERFACES if_serializable_object .
METHODS constructor
IMPORTING
VALUE(it_request) TYPE bapiret2_t .
METHODS get_response
EXPORTING VALUE(it_response) TYPE bapiret2_t.
PROTECTED SECTION.
PRIVATE SECTION.
DATA:
mt_request TYPE bapiret2_t,
mt_response TYPE bapiret2_t.
ENDCLASS.
DATA:
gt_request TYPE bapiret2_t,
gt_response_seq TYPE bapiret2_t,
gt_response_para TYPE bapiret2_t,
gv_diff_seconds_seq TYPE timestampl,
gv_diff_seconds_para TYPE timestampl.
START-OF-SELECTION.
PERFORM ready_test_data.
PERFORM sequence_process.
PERFORM parallel_process.
cl_demo_output=>new( )->write( |Diffrence Seconds: { gv_diff_seconds_seq }|
)->write( gt_response_seq
)->write( |Diffrence Seconds: { gv_diff_seconds_para }|
)->write( gt_response_para
)->display( ).
*&---------------------------------------------------------------------*
*& Form ready_test_data
*&---------------------------------------------------------------------*
*& 准备测试数据
*&---------------------------------------------------------------------*
FORM ready_test_data .
DATA:
ls_request TYPE bapiret2,
lv_index TYPE sy-index.
DO 5 TIMES.
lv_index = sy-index.
DO lv_index TIMES.
ls_request-number = lv_index.
APPEND ls_request TO gt_request.
ENDDO.
ENDDO.
ENDFORM.
*&---------------------------------------------------------------------*
*& Form sequence_process
*&---------------------------------------------------------------------*
*& 顺序执行
*&---------------------------------------------------------------------*
FORM sequence_process .
DATA lt_response TYPE bapiret2_t.
GET TIME STAMP FIELD DATA(lv_timestamp_begin).
LOOP AT gt_request INTO DATA(ls_request).
* 模拟数据库提交操作
CALL FUNCTION 'ZPARA_SLEEP_TEST'
EXPORTING
wait_time = 1
IMPORTING
status = ls_request-type
EXCEPTIONS
internal_error = 1
OTHERS = 2.
APPEND ls_request TO lt_response.
ENDLOOP.
gt_response_seq = lt_response.
GET TIME STAMP FIELD DATA(lv_timestamp_end).
gv_diff_seconds_seq = cl_abap_tstmp=>subtract( tstmp1 = lv_timestamp_end
tstmp2 = lv_timestamp_begin ).
ENDFORM.
*&---------------------------------------------------------------------*
*& Form parallel_process
*&---------------------------------------------------------------------*
*& 多线程执行
*&---------------------------------------------------------------------*
FORM parallel_process .
DATA:
lt_response TYPE bapiret2_t,
lt_request TYPE bapiret2_t,
lt_in_tab TYPE cl_abap_parallel=>t_in_inst_tab,
lt_out_tab TYPE cl_abap_parallel=>t_out_inst_tab.
GET TIME STAMP FIELD DATA(lv_timestamp_begin).
* 按实际需求进行分批,比如同一订单号为一批数据,此处为了测试按照每条数据都并行处理
LOOP AT gt_request INTO DATA(ls_request).
APPEND ls_request TO lt_request.
* 将打包好的数据依次构建传入参数
INSERT NEW zcl_abap_parallel( lt_request ) INTO TABLE lt_in_tab.
CLEAR lt_request.
ENDLOOP.
* p_percentage参数用来指定可用进程百分比,可以指定为0~100
NEW cl_abap_parallel( p_percentage = 95 )->run_inst( EXPORTING p_in_tab = lt_in_tab
IMPORTING p_out_tab = lt_out_tab ).
* 解析执行结果
LOOP AT lt_out_tab INTO DATA(ls_out_tab).
CAST zcl_abap_parallel( ls_out_tab-inst )->get_response( IMPORTING it_response = lt_response ).
APPEND LINES OF lt_response TO gt_response_para.
CLEAR lt_response.
ENDLOOP.
GET TIME STAMP FIELD DATA(lv_timestamp_end).
gv_diff_seconds_para = cl_abap_tstmp=>subtract( tstmp1 = lv_timestamp_end
tstmp2 = lv_timestamp_begin ).
ENDFORM.
*&---------------------------------------------------------------------*
*& CLASS zcl_abap_parallel implementation
*&---------------------------------------------------------------------*
CLASS zcl_abap_parallel IMPLEMENTATION.
* 构造方法
METHOD constructor.
mt_request = it_request.
ENDMETHOD.
* 实际逻辑处理
METHOD if_abap_parallel~do.
DATA:
lv_wait_time TYPE int2.
LOOP AT mt_request ASSIGNING FIELD-SYMBOL(<ls_request>).
* 模拟某条数据处理较慢
IF <ls_request>-number = 1.
lv_wait_time = 2.
ELSE.
lv_wait_time = 1.
ENDIF.
* 模拟业务数据执行
CALL FUNCTION 'ZPARA_SLEEP_TEST'
EXPORTING
wait_time = lv_wait_time
IMPORTING
status = <ls_request>-type
EXCEPTIONS
internal_error = 1
OTHERS = 2.
ENDLOOP.
* 返回处理结果
mt_response = mt_request.
ENDMETHOD.
* 获取处理结果
METHOD get_response.
it_response = mt_response.
ENDMETHOD.
ENDCLASS.
以上。