JavaFX并发和任务(在Task中运行线程)

时间:2015-07-10 14:07:00

标签: multithreading concurrency javafx jssc

我是JavaFx / Concurrency的新手,所以我在Concurrency in JavaFX阅读了教程,但我仍然对JavaFX Gui中后台线程的实现感到困惑。

我正在尝试编写一个与某些串行设备接口的小型GUI(使用JSSC-2.8),并根据这些设备的响应更新GUI。但是,在写入消息和设备响应之间存在滞后,并且使用Thread.sleep()一段任意时间并不是我编程的可靠方法。所以我想使用并发包中的wait()和notify()方法(具有所有适当的同步),但我不确定如何实现它。我最初做的是在Task中创建另一个Thread,它将编写消息并等待响应,并使用一些绑定来更新GUI。我最后把代码包括在内。这是我想要实现的伪代码的简短形式:

start Task:
  connect to serial devices
  synchronized loop: 
    send messages
    wait() for event to fire
      notify()

但是发生的事情是,一旦我调用wait(),整个应用程序就会空闲,然后调用notify()(在响应触发和事件之后),它就不会在它停止的地方继续就此而言,recipe()循环或startTdk()循环,它只是空闲。我是否实现了错误的线程?当我调用wait()时,是否有可能导致EventDispatch或JavaFX应用程序线程暂停?

我希望问题很清楚,如果需要澄清,我可以更新帖子。

public class OmicronRecipe extends Service<String> implements Runnable{

private final String SEPERATOR=";";
private final Tdk tdk;
private final Pvci pvci;
private final SimpleStringProperty data = new SimpleStringProperty(""); 
private final Float MAX_V = 26.0f,UHV=1e-8f;

private boolean isTdkOn=false, isPvciOn=false;
private String power;
private Float temp,press,maxT, setT;
private int diffMaxT,diffP,diffPow, diffT, index=0;

public OmicronRecipe(){
    tdk = new Tdk("COM4");
    pvci = new Pvci("COM5");
}

private synchronized void recipe(){
        while (true){
            try {
                sendMessages();
                data.set(power+SEPERATOR+temp+SEPERATOR+press);
                calcDiffs();
                if (diffPow < 0){
                    if(diffMaxT < 0){
                        if(diffT < 0){
                            if (diffP < 0){
                                if(!rampPow()){
                                    //Max Power reached
                                }
                            }else{
                                //Wait for pressure drop
                            }
                        }
                    }else{
                        //Wait until quit
                    }
                }else{
                    //Max power reached
                }
                Thread.sleep(5000);
            } catch (InterruptedException ex) {
                Logger.getLogger(OmicronRecipe.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
}

private synchronized boolean rampPow(){
    boolean isRamped=false;
    Float setPow = tdk.getSetPow(index), curPow;
    setT = tdk.getSetT(index);
    curPow = Float.parseFloat(power);
    if(curPow.compareTo(setPow) < 0){
        do{
            curPow += 0.1f;
            tdk.sendMessage("PV "+curPow+"\r");
            try {
                wait();
            } catch (InterruptedException ex) {
                Logger.getLogger(OmicronRecipe.class.getName()).log(Level.SEVERE, null, ex);
            }
            curPow = Float.parseFloat(power);
        }while(curPow.compareTo(setPow) < 0);
        index++;
        isRamped=true;
    }
    return isRamped;
}

public synchronized boolean connect(){
    if(!isTdkOn && !isPvciOn){
        isTdkOn = tdk.connect();
        isPvciOn = pvci.connect();
    }
    return isTdkOn && isPvciOn;
}

public synchronized boolean disconnect(){
    if(tdk!=null && pvci !=null){
        isTdkOn = tdk.disconnect();
        isPvciOn = pvci.disconnect();
    }
    return !isTdkOn && !isPvciOn;
}

public synchronized StringProperty getData(){
    return data;
}

public void setMaxT(Float maxT){
    this.maxT = maxT;
}

private synchronized void calcDiffs(){
    Float pow = Float.parseFloat(power);
    diffPow = pow.compareTo(MAX_V);
    diffMaxT = temp.compareTo(maxT);
    diffT = temp.compareTo(100f);
    diffP = press.compareTo(UHV);
}

private synchronized void setListeners(){
    tdk.getLine().addListener((ov,t, t1)-> {
        synchronized (this){
            System.out.println("New Power: "+t1);
            power = t1;
            this.notify();
        }
    });
    pvci.getLine().addListener((ov,t,t1) ->{
        synchronized (this){
        String[] msg = t1.split(SEPERATOR);
        if(msg.length == 2){
            switch(msg[0]){
                case "temperature":
                    System.out.println("Temperaute");
                    temp = Float.parseFloat(msg[1]);
                    break;
                case "pressure":
                    System.out.println("Pressure");
                    press = Float.parseFloat(msg[1]);
                    break;
                default:
                    System.out.println("Nothing; Something went wrong");
                    break;
            }
        }

            this.notify();
        }
    });
}

private synchronized void sendMessages(){
        try {
            tdk.sendMessage("PV?\r");
            this.wait();
            pvci.sendMessage("temperature");
            this.wait();
            pvci.sendMessage("pressure");
            this.wait();
        } catch (InterruptedException ex) {
            Logger.getLogger(OmicronRecipe.class.getName()).log(Level.SEVERE, null, ex);
        }
}

private synchronized boolean startTdk(){
    boolean isOut=false;
        if(isTdkOn){
            try {
                tdk.sendMessage("ADR 06\r");
                this.wait();
                System.out.println("Power: "+power);
                if(power.equals("OK")){
                    tdk.sendMessage("OUT?\r");
                    this.wait();
                    if(power.equals("OFF")){
                        tdk.sendMessage("OUT ON\r");
                        this.wait();
                        isOut = power.equals("ON");
                    }
                    else{
                        isOut = power.equals("ON");
                    }
                }
            } catch (InterruptedException ex) {
                Logger.getLogger(OmicronRecipe.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        return isOut;
}

@Override
protected Task<String> createTask() {

    return new Task<String>() {
          @Override
          protected String call() throws IOException{
            new Thread(new OmicronRecipe()).start();
            return "";
          }
      };

}

@Override
public void run() {
    if (connect()){
        setListeners();
        if(startTdk()){
            recipe();
        }
    }
}
}

我不会包含Pvci类,因为它只是Tdk类的副本,但具有与该机器通信的特定消息序列。

public class Tdk {

private SerialPort tdkPort;
private final String portName;
private StringBuilder sb = new StringBuilder("");;
private final StringProperty line = new SimpleStringProperty("");
private final HashMap<Float,Float> calibMap;
private ArrayList<Float> list ;
private boolean isEnd=false;

public Tdk(String portName){
    this.portName = portName;
    System.out.println("TDK at "+portName);
    calibMap = new HashMap();
    setMap();
}

public synchronized boolean connect(){
    tdkPort = new SerialPort(portName);
    try {
        System.out.println("Connecting");
        tdkPort.openPort();
        tdkPort.setParams(9600,
                SerialPort.DATABITS_8,
                SerialPort.STOPBITS_1,
                SerialPort.PARITY_NONE);
        tdkPort.setEventsMask(SerialPort.MASK_RXCHAR);
        tdkPort.addEventListener(event -> {
            if(event.isRXCHAR()){
                if(event.getPortName().equals(portName)){
                    try {
                        if(!isEnd){
                            int[] str = tdkPort.readIntArray();
                            if(str!=null)
                                hexToString(str);    
                        }
                        if(isEnd){
                            System.out.println("Here: "+sb.toString());
                            isEnd=false;
                            String d = sb.toString();
                            sb = new StringBuilder("");
                            line.setValue(d);

                        }
                    } catch (SerialPortException e) {
                            Logger.getLogger(Tdk.class.getName()).log(Level.SEVERE, null, e);
                    }
                }
            }
        });
    } catch (SerialPortException e) {
            Logger.getLogger(Tdk.class.getName()).log(Level.SEVERE, null, e);
    }
    return tdkPort !=null && tdkPort.isOpened();
}

public synchronized boolean disconnect(){
    if(tdkPort!=null) {
        try {
            tdkPort.removeEventListener();
            if (tdkPort.isOpened())
                    tdkPort.closePort();
        } catch (SerialPortException e) {
                Logger.getLogger(Tdk.class.getName()).log(Level.SEVERE, null, e);
        }
        System.out.println("Disconnecting");
    }
    return tdkPort.isOpened();
}

public synchronized void sendMessage(String message){
    try {
        tdkPort.writeBytes(message.getBytes());
    } catch (SerialPortException e) {
            Logger.getLogger(Tdk.class.getName()).log(Level.SEVERE, null, e);
    }
}

private void setMap(){
    calibMap.put(1.0f, 25.0f);
    calibMap.put(7.0f, 125.0f);
    calibMap.put(9.8f, 220.0f);
    list = new ArrayList(calibMap.keySet());
}

public Float getSetPow(int index){
    return list.get(index);
}

public Float getSetT(int index){
    return calibMap.get(list.get(index));
}

public synchronized StringProperty getLine(){
    return line;
}

private synchronized void hexToString(int[] hexVal){
    for(int i : hexVal){
        if(i != 13){
            sb.append((char)i);
        }else{
            isEnd=true;
        }
    }
    System.out.println("Turning: "+Arrays.toString(hexVal)+" to String: "+sb.toString()+" End: "+isEnd);
}

1 个答案:

答案 0 :(得分:2)

<强>冻结

你的用户界面很可能冻结,因为你正在等待FX Apllication Thread,解决这个问题有不同的方法:

JavaFX应用程序线程

您可以将一些工作委托给FX Application Thread,因此请参阅Platform.runLater

并非所有内容都可以在此线程上运行,但是例如,在您的DeviceController中,您可以等到消息出现,然后调用Platform.runLater()并更新字段(您应该将字段移交给控制器)。

<强>数据绑定 您正在描述的内容也可以通过DataBinding来实现。 有了这个,您可以定义一个SimpleStringProperty,它绑定到您的UI标签(.bind()方法)。如果控制器必须触发其消息,您可以设置StringProperty,UI将自行更新。 您描述的方案可以像这样使用:

public IList<QR_Group> GetAllGroups() {
    return db.GetAllGroups().ToList();
}

我们被教导,并发通知/等待 级别上的并发wait()/ notify()是非常低级别的。您应该尝试使用更高级别的同步方法或帮助程序(人们已经解决了您的问题:))