1

Javaプログラムで3つのスレッドを作成しました。1つはメインプログラムで、他はThreadを拡張する2つのクラスです。メインスレッドは、マシンのコントローラーを表します。もう1つのスレッドはアクチュエーターで、3番目はセンサーです。コントローラは、アクチュエータスレッドによって読み取られる独自のクラスに変数を設定します。アクチュエータは、指示に従って特定のアクションを実行し、自身の内部変数を更新します。これらは、アクチュエーター変数(実世界のアクションを表す)を読み取り、コントローラーによって読み取られる独自の内部変数を設定するセンサースレッドによって順番に読み取られ、完全に一周します。次に、コントローラーは、新しい検出された世界などに応じて変数を設定します。

アクチュエータは、各ループで100ミリ秒スリープする永遠のループにあります。センサーは、ループごとに20msスリープする永遠のループにもあります。

システムはほぼ機能します。メインループもスリープを追加しない限り、センサーからの更新を見逃します。私の質問は、なぜそうなのかということです。sleep(0)でも、システムは機能します。ステートメントをperformJob(Job input)whileループ内に配置しました。スリープ呼び出しのないJavaメインスレッドは、同じスレッドとどのように異なる動作をしますか?並行性は私にとってかなり新しい主題です。

これは私が使用しているコードです:

主要:

public class Main {
    public static void main(String[] args) {
        Controller Press = new Controller();

        Press.processUnits(1);  // no reset code at the moment, only use 1 at a time
        Press.shutdownThreads();
    }
}

コントローラ:

import java.util.LinkedList;

public class Controller extends Thread {
    // Constants
    static final int STATE_WAITING          = 0;
    static final int STATE_MOVE_ARMS        = 1;
    static final int STATE_MOVE_PRESS       = 2;

    static final int LOADINGARM             = 2;
    static final int UNLOADINGARM           = 1;
    static final int NOARM                  = 0;        
    static final boolean EXTEND             = true;
    static final boolean RETRACT            = false;

    private enum Jobs {
        EXTENDRETRACT, ROTATE, MAGNETONOFF, PRESSLOWERRAISE
    }

    // Class variables
    private int currentState;

    // Component instructions
    private int armChoice = 0;
    private boolean bool = false;       // on, up, extend / off, down, retract

    private boolean[] rotInstr = {false, false}; // {rotate?, left true/right false}
    private boolean errorHasOccurred = false;
    private boolean pressDir = false;
    private Sensors sense = null;
    private Actuators act = null;
    private LinkedList<Job> queue = null;

    // Constructor
    public Controller() {
        act = new Actuators(0.0f, this);
        sense = new Sensors();

        act.start();
        sense.start();

        currentState = STATE_WAITING;
        queue = new LinkedList<Job>();
    }

    // Methods

    int[] getArmInstructions() {
        int extret = (bool) ? 1 : 0;
        int[] ret = {armChoice, extret};

        return ret;
    }

    boolean[] getRotation() {
        return rotInstr;
    }

    int getControllerState() {
        return currentState;
    }

    boolean getPressDirection() {
        return pressDir;
    }

    public boolean processUnits(int nAmount) {
        if (run(nAmount)) {
            System.out.println("Controller: All units have been processed successfully.");
            return true;
        } else {    // procUnits returning false means something went wrong
            System.out.println("Controller: An error has occured. The process cannot complete.");
            return false;
        }
    }

    /*
     * This is the main run routine. Here the controller analyses its internal state and its sensors
     * to determine what is happening. To control the arms and press, it sets variables, these symbolize
     * the instructions that are sent to the actuators. The actuators run in a separate thread which constantly
     * reads instructions from the controller and act accordingly. The sensors and actuators are dumb, they
     * will only do what they are told, and if they malfunction it is up to the controller to detect dangers or
     * malfunctions and either abort or correct.
     */

    private boolean run(int nUnits) {
        /*
        Here depending on internal state, different actions will take place. The process uses a queue of jobs
        to keep track of what to do, it reads a job, sets the variables and then waits until that job has finished
        according to the sensors, it will listen to all the sensors to make sure the correct sequence of events is
        taking place. The controller reads the sensor information from the sensor thread which runs on its own
        similar to the controller.

        In state waiting the controller is waiting for input, when given the thread cues up the appropriate sequence of jobs
        and changes its internal state to state move arms

        In Move arms, the controller is actively updating its variables as its jobs are performed,
        In this state the jobs are, extend, retract and rotate, pickup and drop.
        From this state it is possible to go to move press state once certain conditions apply

        In Move Press state, the controller through its variables control the press, the arms must be out of the way!
        In this state the jobs are press goes up or down. Pressing is taking place at the topmost position, middle position is for drop off
        and lower is for pickup. When the press is considered done, the state reverts to move arms,
        */

        //PSEUDO CODE:
        //This routine being called means that there are units to be processed

        //Make sure the controller is not in a blocking state, that is, it shut down previously due to errors
        //dangers or malfunctions

        //If all ok - ASSUMED SO AS COMPONENTS ARE FAULT FREE IN THIS VERSION
        if (!errorHasOccurred) {

            //retract arms
            currentState = STATE_MOVE_ARMS;
            queue.add(new Job(Jobs.EXTENDRETRACT, LOADINGARM, RETRACT));
            queue.add(new Job(Jobs.EXTENDRETRACT, UNLOADINGARM, RETRACT));
            performWork();
            //System.out.println("Jobs added");

            //while there are still units to process
            for (;nUnits != 0; nUnits--) {
                //move the press to lower position, for unloading
                currentState = STATE_MOVE_PRESS;

                //rotate to pickup area and pickup the metal, also pickup processed
                currentState = STATE_MOVE_ARMS;
                queue.add(new Job(Jobs.EXTENDRETRACT, LOADINGARM, EXTEND));
                queue.add(new Job(Jobs.EXTENDRETRACT, UNLOADINGARM, EXTEND));
                performWork();

                //retract and rotate
                queue.add(new Job(Jobs.EXTENDRETRACT, LOADINGARM, RETRACT));
                queue.add(new Job(Jobs.EXTENDRETRACT, UNLOADINGARM, RETRACT));
                performWork();

                //state change, press moves to middle position
                currentState = STATE_MOVE_PRESS;

                //state change back, put the metal on the press, drop processed and pull arms back
                currentState = STATE_MOVE_ARMS;
                queue.add(new Job(Jobs.EXTENDRETRACT, LOADINGARM, EXTEND));
                queue.add(new Job(Jobs.EXTENDRETRACT, UNLOADINGARM, EXTEND));
                queue.add(new Job(Jobs.EXTENDRETRACT, LOADINGARM, RETRACT));
                queue.add(new Job(Jobs.EXTENDRETRACT, UNLOADINGARM, RETRACT));
                performWork();

                //state change, press the metal in upper position
                currentState = STATE_MOVE_PRESS;

                //repeat until done
            }

            //unload final piece

            //move the press to lower position for unload

            //rotate and pickup processed piece

            //drop it off at unloading and wait for more orders
            currentState = STATE_WAITING;
        }

        return true;
    }


    private boolean performWork() {
        while (!queue.isEmpty()) {
                performJob(queue.removeFirst());
        }

        return true;
    }

    private boolean performJob(Job input) {
        //The purpose of this function is to update the variables and wait until they are completed

        // read in the job and check appropriate sensors
        boolean[] data = sense.getSensorData(); // LExt, LRet, UlExt, UlRet
        printBools(data);
        int Instruction = input.Instruction;
        boolean skipVars = false;

        if (input.Job == Jobs.EXTENDRETRACT) {
            if (currentState != STATE_MOVE_ARMS){
                System.out.println("Wrong state in performJob. State is "+currentState+" expected "+STATE_MOVE_ARMS);
                return false;
            }

            if ((Instruction == LOADINGARM) && (input.Bool)) skipVars = data[0];
            if ((Instruction == LOADINGARM) && (!input.Bool)) skipVars = data[1];
            if ((Instruction == UNLOADINGARM) && (input.Bool)) skipVars = data[2];
            if ((Instruction == UNLOADINGARM) && (!input.Bool)) skipVars = data[3];
        }


        if (!skipVars) {
            // if sensors not at intended values, update correct variables
            System.out.println("Controller: Did not skip vars");

            switch (input.Job) {
                case EXTENDRETRACT:
                    armChoice = (Instruction == LOADINGARM) ? LOADINGARM : UNLOADINGARM;
                    bool = input.Bool;  
                    break;

                case ROTATE:
                    break;

                case MAGNETONOFF:
                    break;

                case PRESSLOWERRAISE:
                    break;

                default: 
                    System.out.println("Default called in performJob()");
                    break;
            }
        }

        // listen to sensors until completed
        boolean done = false;


        System.out.println("Waiting for sensor data.");
        //System.out.print("Instruction is "+Instruction+" and bool is "); if (input.Bool) System.out.print("true\n"); else System.out.print("false\n");
        while (!done) {
            data = sense.getSensorData();

            // REMOVING THIS TRY STATEMENT BREAKS THE PROGRAM
            try {
                  Thread.currentThread().sleep(10);
            } catch (InterruptedException e) {
                System.out.println("Main thread couldn't sleep.");
            }

            // Check appropriate sensors
            if (input.Job == Jobs.EXTENDRETRACT) {
                if ((Instruction == LOADINGARM) && (input.Bool)) done = data[0];
                if ((Instruction == LOADINGARM) && (!input.Bool)) done = data[1];
                if ((Instruction == UNLOADINGARM) && (input.Bool)) done = data[2];
                if ((Instruction == UNLOADINGARM) && (!input.Bool)) done = data[3];

            }
        }

        // reset all variables
        armChoice = 0;
        bool = false;

        // when done return
        System.out.println("Finished "+input.Job);
        return true;
    }

    public void shutdownThreads() {
        sense.shutDown();
        act.shutDown();
    }

    private class Job {
        // Class variables
        Jobs Job;
        int Instruction;
        boolean Bool;   // used for directions, up/down, left/right, extend/retract

        // Constructor
        Job(Jobs newJob, int newInstruction, boolean dir) {
            Job = newJob;
            Instruction = newInstruction;
            Bool = dir;
        }
    }

    private void printBools(boolean[] input) {
        System.out.println();
        for (int i = 0; i < input.length; i++) {
            if (input[i]) System.out.print("true "); else System.out.print("false "); 
        }
        System.out.println();
    }
}

アクチュエータ:

public class Actuators extends Thread {
    // Constants
    private final int ARM = 0, ROTATE = 0; // array indexes - which arm, rotate yes/no? 
    private final int DIR = 1, ROTDIR = 1; // array indexes - which direction ext/ret, rotate direction
    private final int EXT = 1;//, RET = 0;
    private final double ARM_SPEED = 5.0;

    // Class variables
    private Controller Owner = null;
    private boolean run = true;

    // Constructor
    Actuators(float nPos, Controller Owner) {
       Reality.changeAngle(nPos);
       this.Owner = Owner;
    }

    // Methods
    private void rotate(boolean dir) {
        float nAngle = dir ? 0.1f : -0.1f;
        Reality.changeAngle(nAngle);
    }

    public void run() {
        while (run) {
            try {
                sleep(100);
            } catch (InterruptedException e) {
                System.out.println("Actuators couldn't sleep");
            }

            // read variables in controller
            int nState = Owner.getControllerState();

            if (nState == Controller.STATE_MOVE_ARMS) {
                boolean[] rot = Owner.getRotation();

                if (rot[ROTATE]) { // Rotation?
                    rotate(rot[ROTDIR]); 
                } else { // or arm extensions
                    int[] instr = Owner.getArmInstructions();

                    if (instr[ARM] != Controller.NOARM) {   // 0 = no arm movement
                        //System.out.println("Actuator arm is "+instr[ARM]);
                        double dir = (instr[DIR] == EXT) ? ARM_SPEED : -ARM_SPEED;  // 1 = extend, 0 = retract
                        Reality.changeArmLength(instr[ARM], dir);
                    }
                }               
            }
        }
    }

    void shutDown() {
        run = false;
    }
}

Realityは、静的フィールドとメソッドで構成されるクラスであり、アクチュエーターによって書き込まれ、センサーによって読み取られます。

public class Reality {
    // Constants
    static private final double EXTEND_LIMIT = 100.0;
    static private final double RETRACT_LIMIT = 0.0;

    // Variables
    private static float ArmsAngle = 0.0f;

    // Read by Sensor
    static double LoadingArmPos = 0.0;
    static double UnloadingArmPos = 0.0;

    // Methods

    static void changeAngle(float newAngle) {
        ArmsAngle = ArmsAngle + newAngle;

        if ((ArmsAngle < 0.0f) || (ArmsAngle > 90.0f))
            System.out.println("Reality: Unallowed Angle");
    }

    static void changeArmLength(int nArm, double dPos) {    // true = extend, false = retract
        switch (nArm) {
            case Controller.LOADINGARM:
                LoadingArmPos += dPos;
                checkArmPos(LoadingArmPos);
                break;

            case Controller.UNLOADINGARM:
                UnloadingArmPos += dPos;
                checkArmPos(UnloadingArmPos);
                break;

            default:
                System.out.println("Arm other than 2 (load) or 1 (unload) in changeArmLength in Reality");
                break;
        }
    }

    static float senseAngle() {
        return ArmsAngle;
    }

    static private boolean checkArmPos(double dPos) {
        // Allowed positions are 100.0 to 0.0
        if ((dPos > EXTEND_LIMIT) || (dPos < RETRACT_LIMIT)) {  
            System.out.println("Arm position impossible in reality. Is "+dPos);
            return true;
        } else {
            return false;
        }
    }
}

最後にセンサー:

public class Sensors extends Thread {
    // Constants
    private final double EXTENDED = 100.0;
    private final double RETRACTED = 0.0;
    private final double MARGIN = 0.1;

    // Class Variables
    private boolean run = true;

    // Read by Controller
    private boolean LoadingExtended = true;
    private boolean LoadingRetracted = true;
    private boolean UnloadingExtended = true;
    private boolean UnloadingRetracted = true;

    // Constructor
    Sensors() {         
        LoadingExtended = false;
        LoadingRetracted = true;
        UnloadingExtended = false;
        UnloadingRetracted = true;
    }


    // Methods
    boolean senseLoadingExtended() {
        return (Math.abs(Reality.LoadingArmPos - EXTENDED) < MARGIN);
    }

    boolean senseLoadingRetracted() {
        return (Math.abs(Reality.LoadingArmPos - RETRACTED) < MARGIN);
    }

    boolean senseUnloadingExtended() {
        return (Math.abs(Reality.UnloadingArmPos - EXTENDED) < MARGIN);
    }

    boolean senseUnloadingRetracted() {
        return (Math.abs(Reality.UnloadingArmPos - RETRACTED) < MARGIN);
    }

    // called by Controller
    boolean[] getSensorData() {
        boolean[] ret = {LoadingExtended, LoadingRetracted, UnloadingExtended, UnloadingRetracted};
        return ret;
    }

    // Sensor primary loop
    public void run() {
        while (run) {
            try {
                sleep(20);
            }
            catch (InterruptedException e) {
                System.out.println("Sensors couldn't sleep");
            }

            LoadingExtended = senseLoadingExtended();
            LoadingRetracted = senseLoadingRetracted();
            UnloadingExtended = senseUnloadingExtended();
            UnloadingRetracted = senseUnloadingRetracted();
        }
    }

    void shutDown() {
        run = false;
    }

}

このバージョンでは、すべてのフィールドと関数が読み取られるわけではありません。このプログラムは、主に関数呼び出しを使用して、以前のシングルスレッドアプリケーションを作り直したものです。読みやすくするためにコードを少しクリーンアップしました。元々の質問ではありませんでしたが、建設的なデザインの発言は大歓迎です。本当に怪しいことが起こっています。私は通常、迷信的なコーダーではありませんが、たとえば、sleep呼び出しをSystem.out.println()呼び出しに置き換えることができ、プログラムは機能します。

4

3 に答える 3

1

ここで発生したのは、おそらくメモリ整合性エラーでした。コントローラークラスが内部制御変数を設定し、センサーを待機するループに入った場合、アクチュエータークラスとセンサークラスがコントローラーに表示される読み取り値を適切に更新できず、コントローラーが正しい値を表示できなかった可能性があります。別のクラスから読み取るすべての関数にsynchronizeステートメントを追加することで、問題が解決されました。スリープ呼び出しで、コントローラースレッドが何らかの同期ブロックに入り、他のスレッドの変数への変更が表示されるようになったと推測できます。

于 2012-09-16T17:26:46.283 に答える
1

Googlefor'プロデューサーコンシューマーキュー'。

待ち時間、非効率性、データの損失が必要な場合を除いて、スレッド間通信にsleep()を使用しないでください。sleep()を回避し、共有/ロックされたオブジェクトから有効なデータを直接読み取ろうとする、はるかに優れたメカニズムが利用可能です。

'comms'オブジェクトにcommands/requests / dataをロードし、それらを他のスレッドにキューイングし、すぐに別のcommsオブジェクトを作成して後続の通信を行う場合、スレッド間通信は高速で安全になり、sleep()の遅延やチャンスはありません。古くなっているか、別のスレッドによって変更されているデータを読み取るスレッドの。

于 2012-07-10T04:46:01.123 に答える
0

どのJVMを使用していますか?

迅速な回避策として、スレッド間で共有されるフィールドにvolatileを設定できます。

また、メッセージングに関するアクターのアプローチも参照してください:http: //doc.akka.io/docs/akka/snapshot/java/untyped-actors.html

于 2012-07-10T04:44:47.457 に答える