在本文中,我想谈谈多线程功能的使用,该功能在许多地方都作为性能拯救了我们。
如果我们必须有序地处理某个数据块,我们在性能方面可能会束手无策。为了说明一个场景,我们可能需要对循环中的记录执行以下操作。
- 使用 Bapi Ex 读取记录:读取材料的产品树
- 处理 Excel 行
- 使用 Bapi 将记录扔进系统
让我们看看如何在这种情况和类似情况下使用我们的救星多线程。基本逻辑是将我们将要执行的操作分成多个部分并并行处理它们。
注意:请勿将此方法用于非常短的操作,因为并行处理的任务管理在 CPU 方面非常耗时。例如,如果您只并行阅读材料的文本,则可能需要更长的时间。
示例场景:为简单起见,让我们编写一个 RFC,该 RFC 返回材料的订单、交货、发票项数。然后,让我们针对每种材料单独并行调用此函数。让我们测量一下他们的工作时间。
步骤 1:RZ12 - RFC 服务器组创建
我建议您从 RZ12 事务代码创建一个 RFC 服务器组。您也可以使用现有的。有关此处参数的详细信息,您可以查看注释527481和 74141。我创建了一个名为 390 的新 RFC 服务器组,如下所示。
步骤 2:RFC 函数
让我们编写一个我们将处理的 RFC(远程启用)。
ABAP
FUNCTION zms_material_read.
*"----------------------------------------------------------------------
*"*"Local Interface:
*" IMPORTING
*" VALUE(IV_MATNR) TYPE MATNR
*" EXPORTING
*" VALUE(EV_DELIVERY_ITEMS) TYPE I
*" VALUE(EV_INVOICE_ITEMS) TYPE I
*" VALUE(EV_ORDER_ITEMS) TYPE I
*"----------------------------------------------------------------------
SELECT COUNT(*) INTO ev_delivery_items FROM lips WHERE matnr eq iv_matnr.
SELECT COUNT(*) INTO ev_invoice_items FROM vbrp WHERE matnr eq iv_matnr.
SELECT COUNT(*) INTO ev_order_items FROM vbap WHERE matnr eq iv_matnr.
ENDFUNCTION.
步骤 3:让我们在程序中使用它
现在我们所要做的就是并行使用空闲的 CPU 任务,并使进程更快地完成。
当我们对 100 种材料执行此操作时;
单例处理 SM50 视图
并行处理 SM50 视图
结果;我们得到的结果比:)快了大约 6 倍我在下面包含了整个程序。
ini
REPORT zms_par.
DATA : gt_matnr TYPE TABLE OF mara-matnr WITH HEADER LINE,
gv_completed TYPE i."tamamlanan task lar
CONSTANTS c_pargroup TYPE rfcgr VALUE 'PARTEST'."paralel işleme grubu
SELECTION-SCREEN BEGIN OF BLOCK b1 WITH FRAME TITLE text-001.
PARAMETERS : p_count TYPE i DEFAULT 10.
SELECTION-SCREEN END OF BLOCK b1.
*--------------------------------------------------------------------*
START-OF-SELECTION.
*--------------------------------------------------------------------*
* malzeme listesi
SELECT matnr INTO TABLE gt_matnr FROM mara UP TO p_count ROWS.
* tekil okuma
PERFORM unique_read.
* Paralel okuma
PERFORM parallel_read.
*&---------------------------------------------------------------------*
*& Form UNIQUE_READ
*&---------------------------------------------------------------------*
FORM unique_read .
DATA : lv_time_begin TYPE i,
lv_time_end TYPE i,
lv_time_diff TYPE i.
GET RUN TIME FIELD lv_time_begin.
LOOP AT gt_matnr.
CALL FUNCTION 'ZMS_MATERIAL_READ'
EXPORTING
iv_matnr = gt_matnr.
ENDLOOP.
GET RUN TIME FIELD lv_time_end.
lv_time_diff = ( lv_time_end - lv_time_begin ) / 1000000.
WRITE : 'Tekil okuma süresi ', 30 lv_time_diff , ' saniye ...'.
ENDFORM. " UNIQUE_READ
*&---------------------------------------------------------------------*
*& Form PARALLEL_READ
*&---------------------------------------------------------------------*
* text
*----------------------------------------------------------------------*
* --> p1 text
* <-- p2 text
*----------------------------------------------------------------------*
FORM parallel_read .
DATA : lv_freetask TYPE i,
lv_taskname TYPE char20,
lv_taskid(8) TYPE n,
lv_sended TYPE i,
lv_completed TYPE i.
*--------------------------------------------------------------------*
* Zaman ölçümü için
DATA : lv_time_begin TYPE i,
lv_time_end TYPE i,
lv_time_diff TYPE i.
GET RUN TIME FIELD lv_time_begin.
*--------------------------------------------------------------------*
PERFORM get_freetask CHANGING lv_freetask.
IF lv_freetask GT 1.
LOOP AT gt_matnr.
ADD 1 TO lv_taskid.
CONCATENATE 'T' lv_taskid INTO lv_taskname.
TRY.
CALL FUNCTION 'ZMS_MATERIAL_READ'
STARTING NEW TASK lv_taskname
DESTINATION IN GROUP c_pargroup
PERFORMING return_back_this_task
ON END OF TASK
EXPORTING
iv_matnr = gt_matnr
EXCEPTIONS
communication_failure = 1
system_failure = 2
resource_failure = 3
error_message = 4.
IF sy-subrc <> 0.
IF sy-subrc EQ 3.
lv_completed = gv_completed.
WAIT UNTIL lv_completed LT gv_completed.
CONTINUE.
ELSE.
ADD 1 TO lv_sended.
"hatalı kayıt burada loglanıp tekil işlenmelidir!
ENDIF.
ELSE.
ADD 1 TO lv_sended.
ENDIF.
CATCH cx_root.
ADD 1 TO lv_sended.
"hatalı kayıt burada loglanıp tekil işlenmelidir!
ENDTRY.
ENDLOOP.
ELSE.
PERFORM unique_read."boşta proses yok ise tekil oku
ENDIF.
TRY .
WAIT UNTIL gv_completed EQ lv_sended .
CATCH cx_root.
"hatalı kayıt burada loglanıp tekil işlenmelidir!
ENDTRY.
GET RUN TIME FIELD lv_time_end.
lv_time_diff = ( lv_time_end - lv_time_begin ) / 1000000.
WRITE : / 'Paralel okuma süresi ', 30 lv_time_diff , ' saniye ...'.
ENDFORM. " PARALLEL_READ
*&---------------------------------------------------------------------*
*& Form return_back_this_task
*&---------------------------------------------------------------------*
FORM return_back_this_task USING taskname ."#EC CALLED
DATA ls_mat TYPE bapimatdoa.
RECEIVE RESULTS FROM FUNCTION 'ZMS_MATERIAL_READ'
* IMPORTING
* ........... "sonuçlar burada alınabilir..
EXCEPTIONS
resource_failure = 1
system_failure = 2
communication_failure = 3
OTHERS = 4.
IF sy-subrc NE 0.
WRITE : / 'PAR_ERROR' , taskname.
"hatalı kayıt burada loglanıp tekil işlenmelidir!
ELSE.
ADD 1 TO gv_completed.
ENDIF.
ENDFORM. "return_back_this_task
*&---------------------------------------------------------------------*
*& Form GET_FREETASK
*&---------------------------------------------------------------------*
FORM get_freetask CHANGING cv_freetask.
CALL FUNCTION 'SPBT_INITIALIZE'
EXPORTING
group_name = c_pargroup
IMPORTING
free_pbt_wps = cv_freetask
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.
ENDFORM.
并行处理时 RM_FREE_SESSION_CHECK 进行资源检查
当您需要调用 FM 并选择开始新任务时,始终存在用户已打开最大数量的会话的风险,因此我们的呼叫可能会失败。有一种方法可以避免这种风险 -> 轻松直接调用 FM RM_FREE_SESSION_CHECK
或 TH_USER_INFO
来检查我们是否有能力打开新会话。如果是,那么我们可以在新任务中调用 FM,如果不是,那么我们可以引发错误消息并停止处理我们当前正在执行的函数。当您需要在用户出口内的新任务中调用 FM 时,这非常有用。
ini
data: gt_return type bapiret2_tt.
call function 'RM_FREE_SESSION_CHECK'
exceptions
no_free_session = 1
others = 2.
if sy-subrc eq 0.
"Z FM with standard one iside to be able to do commit & rollback
call function 'Z_BAPI_GOODSMVT_CREATE'
starting new task 'NEW_TASK'
performing check_return on end of task
exporting
goodsmvt_header = fs_goodsmvt_header
goodsmvt_code = fs_goodsmvt_code
testrun = space
* GOODSMVT_REF_EWM =
* IMPORTING
* GOODSMVT_HEADRET =
* MATERIALDOCUMENT =
* MATDOCUMENTYEAR =
tables
goodsmvt_item = ft_goodsmvt_item
* GOODSMVT_SERIALNUMBER =
return = ft_return
* GOODSMVT_SERV_PART_DATA =
* EXTENSIONIN =
.
wait until g_separatetask_done eq 'X' up to 120 seconds.
else.
message e027(14).
* Maximum number of sessions reached
endif.
* Rest of the code goes here
*-----------
* form that is called after task is complete
form check_return using taskname.
receive results from function 'Z_BAPI_GOODSMVT_CREATE'
tables
return = gt_return.
g_separatetask_done = 'X'.
endform. "check_return