SAP ABAP 多线程处理/并行处理的四种方式

前言

在 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.

以上。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DeveloperMrMeng

觉得有用的佛系投币哦

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值