简体   繁体   中英

How to interrupt a task in progress with a timeout mechanism?

I'm currently developping a ftp client based on the package commons.net, in order to run tests to see the speed of the connection. Basically my ftp test consists in connect to the server, logging onto it, and then start a cycle of download/upload as long as necessary, until the user decides to stop it via a button, then the current cycle will end and so will the test.

However, while running those tests, a situation requiering a timout mechanism has occured. the server was transmitting the file, and send the return code 226 (transfer complete) before it was indeed completed.

So my thread remains stuck, trying to empty the inputStream when it is not possible anymore.

My idea was to start a threaded timer with the downloading process, that will be reset each time a byte is transferred to my client. When the timeout occurs, then an exception or so would be raised, and my client would react to it, abording the download.

I have read and try many solutions, among them: - raising an exception from a thread -> the thread catches the exception and not the client; - interrupt the client from the thread, so the client raises itself an interruptedException -> doesn't seem to work; - using an executor with a timeout -> since I can't know the "normal" duration of a download, I can't give it to the executor when I start the task, moreover, the timer has to be reset when I receive data.

I read a lot about it on many forums, and didn't find any solution that seem to be adapted AND work in this case. If anyone has an idea of another way to do it?

This is the code of the action I am performing:

public double get(String fileName) {
    [...]
    org.apache.commons.net.io.Util.copyStream(stO,stD,client.getBufferSize(),
                this.localSize,
                new org.apache.commons.net.io.CopyStreamAdapter() {
                    public void bytesTransferred(long totalBytesTransferred,
                            int bytesTransferred,long streamSize) {
                        setProgressDL(totalBytesTransferred);
                        //reset the timer here
                    }
        });
[...]
}

Here is some of the code of my test, launching my client:

public class TestFtp extends Thread {
[...]
public void run() {
    System.out.println("Launching FTP test");
    FtpClient client = new FtpClient(this.model, this, this.model.getFtpServer());
    try {
                    //Attempting connection on the server
        client.connect();
        try {
            // Attempting login
            client.login(this.model.getUsername(), this.model.getPassword());
            do {
                client.changeDirectory("get");
                                    // start timer
                client.get(this.model.getDistantFileName());
                                    // stop timer

                client.changeToParentDirectory();
                client.changeDirectory("put");
                client.set(this.model.getDistantFileName(),
                        this.model.getNewFileName());

                client.changeToParentDirectory();
                try {
                    // Little pause between each test
                    Thread.sleep(500);
                } catch (InterruptedException e) {}
                // Continue test until the user stops it
            } while (this.continuous);
            // Once the test is over, logout
            client.logout();

            } catch (FTPLoginException e) {
            // If login fails, the test ends
            System.out.println("Unable to login to the server.");
        }
    } catch (FTPConnectException e) {
        // If connection error, the test ends
        System.out.println("Unable to connect to the server.");
    }
}

Thank you by advance if anyone can help, and if you need further information on my actual code, I can put more of it in here.

Well, I'm sorry but I admit I haven't read all your code, but if you want to interrupt a running thread, do two things:

  1. run the thread code inside a try/catch block like this:

Example:

public void run() {
    try {
        // code to run
    } catch (InterruptedException ie) {
        // thread interrupted, may want to do some clean up
        // but must return as quickly as possible to avoid halting external code
    }
}
  1. Call the interrupt() method of the thread above externally when the need arises.

Example:

thread.interrupt();

This will tell the VM to throw the InterruptedException in your thread no matter what it's doing, giving you a chance to do some stuff.

I hope this is what you're looking for...

EDIT

Ok, a concrete example that works:

public class Driver {

private static int count = 0;

public static void main(String[] args) {

    Thread t = new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                bigTask();
            } catch (InterruptedException ie) {
                System.out.println("Interrupted thread! Count is " + count);
            }
        }
    });

    t.start();

    try {
        Thread.sleep(1000);
        System.out.println("Trying to interrupt thread");
        t.interrupt();
    } catch (InterruptedException e) {}

}

private static void bigTask() throws InterruptedException {
    List<BigDecimal> bigs = new ArrayList<BigDecimal>();
    for (int i = 0; i < 10000000; i++) {
        bigs.add(BigDecimal.valueOf(i));
        if (Thread.interrupted()) {
            throw new InterruptedException();
        }
        count = i;
    }
    System.out.println("Ok, added ten million items, count is " + count);
}

}

If you do not want to throw unecessary Exceptions, you should use a boolean flag that controls the execution of the thread (or runnable):

public class TestFtp extends Thread {
[...]
boolean abort;
public void run() {
    [...]
    do{
      [...]
    } while (this.continuous && !abort);
    if (abort){
      // You might want to do something here
    }else{
      // The stuff you normally do
    }
}
}

And then simply set the abort flag to false from outside. This way you can better control how you thread will terminate, as thread.interrupt(); will have an undefined behavior.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM