zoukankan      html  css  js  c++  java
  • abap 多线程

    参考http://blog.csdn.net/zhongguomao/article/details/8963815

    参考程序 ZFC_MM030C

    实际项目实施过程中,我们会遇到程序性能优化的问题,这里介绍一种方法:通过RFC接口进行远程函数的异步调用实现程序的并行处理。

       同步/异步调用函数语法

    同步调用:CALL FUNCTION 'AAA' ;

    同步调用的实质:程序进行单线程执行。

    异步调用:CALL FUNCTION 'AAA' STARTING NEWTASK <taskname> "任务名称

    DESTINATION IN GROUP <RFC Serve Group>

                             PERFORMING <subroutine>ON END OF TASK"子程序

        异步调用的实质:程序进行多线程执行。

    一些关于函数异步调用实现程序并行处理的文章,没有介绍如下问题:

    ⒈ 为了避免相同程序重复运行产生的后台任务相互冲突,需要保证在相同时间段同一程序只被一个用户占用;

    ⒉ 异步调用获取的最终结果数据与同步调用获取的结果存在差异;

    ⒊ 固定RFC Server Group如system 'parallel_generators'无法保证程序在不同服务器中通用性。

    问题1分析:从

    MD01中运行MRP我们可以知道,系统为了避免相同程序并发执行,导致后台任务冲突, MD01在

    并行模式下是不允许被两个用户同时执行的。如下图

    解决方法:通过在程序中利用

    锁对象来达到程序相同时间段只被同一用户占用的目的。

    问题2分析:在LOOP循环中采用异步调用函数的模式,通过SY-SUBRC = 0来判断任务启动成功,当SY-SUBRC <> 0时,则获取先前启动的进程返回的值,但是这样就遇到一个问题:如第N次循环正好分配给程序的进程被占用完,这样本次无法启动一个任务进程,导致本次的原始数据通过函数无法获取目标,从而最终结果出现数据不完整和数值不断变化的现象。

    解决方法:牺牲部分性能保证数据的完整。通过RZ12获取服务器的Max. requests in queue 的值,LOOP循环的时候统计启动的启动的进程数是否 = Max. requests inqueue,如果等于则获取先前启动的进程返回的值,然后再重新启动进程,重复此操作。系统分配给每个程序的最大进程数> Max. requests in queue,但是把启动的进程数限制在Max.requests in queue的水平可以保证获取结果的完整性。如下图所示

    问题3分析:一般系统直接指定<RFC Serve Group> =' parallel_generators ',如上图的“服务器组”对应的内容,为了保持一般性通过如下逻辑段获取

    CALL 'C_SAPGPARAM'  "#EC CI_CCALL                     ID 'NAME'  FIELD 'rdisp/myname'
        ID 'VALUE' FIELD g_applserver.  "
      SELECT SINGLE classname
        FROM rzllitab
        INTO g_classname   "Server Group Name
        WHERE applserver = g_applserver
        AND grouptype = 'S'.   "S:服务器组,空:登陆组

    下面通过例子来说明函数异步调用,这里使用函数        

     'MD_STOCK_REQUIREMENTS_LIST_API'获取MD04中的物料+工厂 MRP数据明细。首先我们使用同步调用的方法,然后再使用异步调用的方法,比较二者在同等条件下的执行效率。

    (假设需求:从物料工厂表MARC中获取一定的行项目,这里设定为18000条,然后通过调用函数获取物料工厂对应的MRP清单,将物料工厂与获取的对应清单整合输出)

    同步调用:(完整程序在程序最后)

    select matnr werks

        UP TO 18000 ROWS               "数据条目18000
        INTO CORRESPONDING FIELDS OF TABLE it_marc
        FROM marc
        WHERE matnr IN s_matnr
        AND   werks IN s_werks.
      LOOP AT it_marc INTO wa_marc.
        CLEAR it_md.
        CALL FUNCTION  'MD_STOCK_REQUIREMENTS_LIST_API'
        EXPORTING
      MATNR                    = wa_marc-matnr
            WERKS                    = wa_marc-werks
          TABLES
            MDEZX                    = it_md
          EXCEPTIONS
            MATERIAL_PLANT_NOT_FOUND = 1
            PLANT_NOT_FOUND          = 2
            OTHERS                   = 3.
     
        LOOP AT it_md INTO wa_md.
          MOVE-CORRESPONDING wa_md TO wa_output.
          wa_output-matnr = wa_marc-matnr.
          wa_output-werks = wa_marc-werks.
          APPEND wa_output TO it_output.
          CLEAR:wa_output,wa_md.
        ENDLOOP.
        CLEAR wa_marc.
      ENDLOOP.

    很显然在同步执行情况下,只有一个进程是执行我们调用的函数,其他的进程处于idle状态。

     

    异步调用:

    解决问题1:为了达到当前用户可以独占程序,进入选择界面即锁定程序:

    AT SELECTION-SCREEN.
      "锁定程序
      CALL FUNCTION 'ENQUEUE_EZZSOPR0032'
        EXPORTING
          mode_trdir     = 'E'         "锁类型
          name           = 'ZSOPR0032' "锁对象名称
          x_name         = ' '
          _scope         = '2'
          _wait          = ' '
          _collect       = ' '
        EXCEPTIONS
          foreign_lock   = 1
          system_failure = 2
          OTHERS         = 3.
      IF sy-subrc <> 0.
        MESSAGE '对象已被锁定,请稍后执行' TYPE 'E'.
      ENDIF.

    解决问题1:当程序异步调用函数的操作结束后,即可接触对程序的锁定:

    END OF SELECTION.
      "解除程序的锁定
      CALL FUNCTION 'DEQUEUE_EZZSOPR0032'
        EXPORTING
          mode_trdir = 'E'
          name       = 'ZSOPR0032'
          x_name     = ' '
          _scope     = '3'
          _synchron  = ' '
          _collect   = ' '.

    解决问题3:获取RFC Serve Group name

    获取 RFC Serve Group name         Start--*
    * 一般系统默认g_classname = 'parallel_generators',但为了通用性按照如下方法获取
      CALL 'C_SAPGPARAM'                                      "#EC CI_CCALL
        ID 'NAME'  FIELD 'rdisp/myname'
        ID 'VALUE' FIELD g_applserver.
     
      SELECT SINGLE classname
        FROM rzllitab
        INTO g_classname   "Server Group Name
        WHERE applserver = g_applserver
        AND grouptype = 'S'.   "S:服务器组,空:登陆组
    获取 RFC Serve Group name         End--*

    解决问题2:通过 最大请求队列的值 来控制启动进程数

    SELECT matnr werks

        UP TO 18000 ROWS               "数据条目18000
        INTO CORRESPONDING FIELDS OF TABLE it_marc
        FROM marc
        WHERE matnr IN s_matnr
    AND   werks IN s_werks.
     

    LOOP AT it_marc INTO wa_marc.

    *   生成任务名称 = 'Task' + sy-tabix  Start--*

        WRITE sy-tabix TO g_taskname.

        CONDENSE g_taskname.

        CONCATENATE 'Task' g_taskname INTO g_taskname.

    *   生成任务名称 = 'Task' + sy-tabix   End--*

        lw_marc-taskname = g_taskname.

        lw_marc-matnr = wa_marc-matnr.

        lw_marc-werks = wa_marc-werks.

        APPEND wa_marc TO lt_marc.

    *   异步调用函数    Start--*

        CALL FUNCTION 'MD_STOCK_REQUIREMENTS_LIST_API' STARTINGNEW TASK g_taskname

            DESTINATION INGROUPg_classname

            PERFORMING frm_subroutine_done ONEND OF TASK"子程序

    *      只要将函数的EXPORTING参数放在此处,其他参数放到子程序中

            EXPORTING

              matnr                 = wa_marc-matnr

              werks                 = wa_marc-werks

    *       系统标准报错信息

            EXCEPTIONS

              communication_failure = 1  MESSAGE mess

              system_failure        = 2 MESSAGE mess

              resource_failure      = 3.

            IF sy-subrc = 0.

              snd_jobs = snd_jobs + 1.

            ENDIF.

    *   异步调用函数     End--*

        open_task_num = open_task_num + 1.   "记录启动的进程数量

        IF open_task_num = p_wp.    "p_wp = RZ12中的 Max. requests in queue

    *    获取并发进程返回的结果

          WAIT UNTIL rcv_jobs >= snd_jobs.

          CLEAR:open_task_num,rcv_jobs,snd_jobs.

          FREE:lt_marc.

        ENDIF.

        CLEAR wa_marc.

      ENDLOOP.

    *&---------------------------------------------------------------------*

    *&      Form FRM_SUBROUTINE_DONE

    *&---------------------------------------------------------------------*

    FORM frm_subroutine_done USING g_taskname.

     

      rcv_jobs = rcv_jobs + 1.  "Receiving data

      CLEAR:it_md[].

      RECEIVE RESULTS FROMFUNCTION 'MD_STOCK_REQUIREMENTS_LIST_API'

         TABLES

           mdezx                    = it_md

         EXCEPTIONS

           material_plant_not_found = 1

           plant_not_found          = 2

           OTHERS                   = 3.

     

      functioncall1 = done.

     

      SORT lt_marc BY taskname.

      LOOP AT it_md INTO wa_md.

        READ TABLE lt_marc INTO lw_marc WITH KEY taskname = g_taskname BINARYSEARCH.

        MOVE-CORRESPONDING wa_md TO wa_output.

        wa_output-matnr = lw_marc-matnr.

        wa_output-werks = lw_marc-werks.

        APPEND wa_output TO it_output.

     

        CLEAR:wa_output,wa_md,lw_marc.

      ENDLOOP.

    ENDFORM.                    " FRM_SUBROUTINE_DONE

  • 相关阅读:
    Linux环境部署项目引起Out of Memory Error: PermGen Space的解决方案
    手动上传图片到nginx下可访问,程序上传后访问图片报403
    Spring的断言工具类Assert的基本使用
    前后台分离部署时,Niginx上的部署
    GDAL VS2010 win7(64位)安装、使用说明(图文解析)
    转:libc6-dbg libc库调试的时候需要安装该库获得debug information 才能step into
    转:详解linux中的strings命令简介
    转:浮点数环境 cfenv(fenv.h)
    转:C语言中关于float、double、long double精度及数值范围理解
    转:linux中fork()函数详解
  • 原文地址:https://www.cnblogs.com/springzt/p/4108615.html
Copyright © 2011-2022 走看看