0

我在后台运行了很长时间的操作,例如上传内容、转换图像、音频、视频等。如果用户要求完全停止操作,我想停止/取消它们。

怎么能做到这一点?这有设计模式吗?

注意:有些运行代码可以取消,有些不能。我如何找到一个折衷方案?

编辑:我应该说我希望操作立即停止。

4

4 回答 4

3

总结和扩展乔恩所说的话:

  • 您应该让线程知道它应该退出循环(易失性标志)。
  • 如果您希望线程退出阻塞状态,您可以interrupt()使用该线程。
  • 您应该处理方法InterruptedException内部run
  • 当你被打断时你应该优雅地退出(即完成你正在做的任何事情并清理)​​。

一些代码:

private volatile bool _running;// volatile guarantees that the flag will not be cached

public void kill(){_running = false;}
public void run()
{
    while(_running)
    {        
        try
        {
            DoWork(); // you may need to synchronize here
        }
        catch(InterruptedException e)
        {
            // Handle e
        }
    }
}
于 2011-08-01T16:38:45.207 回答
2

(我假设您已经在单独的线程中执行后台工作。)

boolean基本上,您保留一个UI 线程可以设置的共享标志,并且后台线程会定期读取。当旗帜说“停止”时,你停止:)

请注意,该标志应该是易失的,或者您应该使用锁以确保后台线程肯定“看到”从 UI 线程写入的更改。

它相对粗糙,感觉有点“手动”,但这意味着您不会因为中途中止操作而冒着不稳定的风险,这与Thread.stop().

于 2011-08-01T16:10:39.647 回答
0

我的 2 美分。可取消的任务。和cancelImpl()需要runImpl()实施。

import java.util.concurrent.CancellationException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

public abstract class CancelableTask extends Observable<CancelableTask>
        implements Runnable {
    private volatile boolean isStarted = false;
    private volatile boolean isCanceled = false;
    private volatile boolean isSuccess = false;
    private volatile Exception e;
    private volatile AtomicBoolean doneLock = new AtomicBoolean(false);
    protected final AtomicInteger progress = new AtomicInteger(0);

    public CancelableTask() {
    }

    @Override
    public final void run() {
        try {
            runUnsafe();
        } catch (Exception e) {
            Config.getLog().i("CancelableTask threw an exception", e);          
        }
    }

    public final void runUnsafe() throws Exception {
//      Config.getLog().d("Running cancelable task: " + toString());
        notifyObservers(this);
        isStarted = true;
        try {
            if (!isCanceled) {
                runImpl();
            }
        } catch (Exception e) {
            // Note: Cancel may throw exception
            if (doneLock.compareAndSet(false, true)) {
                this.e = e;
                notifyObservers(this);
                clearObservers();
                // Someone else should do something with the exception
//              Config.getLog().i("Failed cancelable task: " + toString(), e);
                throw e;
            }
            // Cancel got to the lock first but may NOT have yet changed the cancel flag.
            // Must throw cancellation exception.
        }
        if (doneLock.compareAndSet(false, true)) {
            isSuccess = true;
            progress.set(100);
            notifyObservers(this);
            clearObservers();
//          Config.getLog().d("Finished cancelable task: " + toString());
            return;
        }

        // The task was canceled but the isCanceled may not have been set yet.

        synchronized (doneLock) { // Waiting for the cancel to finish it's logic
        }

//      assert isCanceled; // Commented out because android crashes the app in assertion 
        // No need to notify here because cancel was already notified in
        // cancel method.
        // notifyObservers(this);
//      Config.getLog().d("Already canceled task: " + toString());
        throw new CancellationException("Canceled while running!");
    }

    protected abstract void runImpl() throws Exception;

    protected void cancelImpl() {}

    public final void cancel() {
        synchronized (doneLock) {
            if (doneLock.compareAndSet(false, true)) {
//              Config.getLog().i("Canceling cancelable task: " + toString());
                isCanceled = true;

                cancelImpl();

                notifyObservers(this);

                clearObservers();
            }
        }
    }

    public final boolean isCanceled() {
        return isCanceled;
    }

    public final boolean isSuccessful() {
        return isSuccess;
    }

    public final boolean isDone() {
        return doneLock.get();
    }

    public final boolean isStarted() {
        return isStarted;
    }

    public final Exception getError() {
        return e;
    }

    public int getProgress() {
        return progress.get();
    }

    /**
     * Observers will be cleared after the task is done but only after all of them are notified.
     */
    @Override
    public void addObserver(Observer<CancelableTask> observer) {
        super.addObserver(observer);
    }


//  protected void incrementProgress(int value) {
//      progress += value;
//  }
}

还有 CancelableCollection:

import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map.Entry;
import java.util.concurrent.CancellationException;

public class CancelableCollection extends CancelableTask {
    private LinkedHashMap<CancelableTask, Integer> cancelables = new LinkedHashMap<CancelableTask, Integer>();
    private volatile boolean normalizing;
    private volatile State state = new State(null, 0, 0);
    private boolean isOneFailsAll = true;

//  public boolean isOneFailsAll() {
//      return isOneFailsAll;
//  }

    public void setOneFailsAll(boolean isOneFailsAll) {
        this.isOneFailsAll = isOneFailsAll;
    }

    public int getTotalWeight() {
        Collection<Integer> values = cancelables.values();
        int total = 0;
        for (int weight : values) {
            total += weight;
        }
        return total;
    }

    /**
     * Adds and runs the cancelable
     * 
     * @param cancelable
     * @return
     * @throws Exception
     *             if failed while running
     * @throws CancellationException
     *             if already canceled
     */
    public void add(CancelableTask cancelable, int relativeTime) {
        if (cancelable == null) {
            return;
        }
        cancelables.put(cancelable, relativeTime);

        if (isCanceled()) {
            throw new CancellationException("Canceled while running!");
        }

        if (isDone()) {
            throw new RuntimeException(
                    "Cannot add tasks if the Cancelable collection is done running");
        }

        if (normalizing) {
            throw new RuntimeException(
                    "Cannot add tasks if already started normalizing");
        }
    }

    @Override
    protected void runImpl() throws Exception {
        normalizeProgress();
        for (Entry<CancelableTask, Integer> entry : cancelables.entrySet()) {
            int currentRelativeTime = entry.getValue();
            CancelableTask currentTask = entry.getKey();
            // Advance the state to the next one with the progress from the
            // previous one.
            state = new State(currentTask, currentRelativeTime, state.getProgress());
            try {
                currentTask.runUnsafe();
            } catch (Exception e) {
                if (isOneFailsAll) {
                    throw e;
                }
                Config.getLog().i("Task failed but continueing with other tasks", e);
            }
        }
        state = new State(null, 0, 100);
    }

    private void normalizeProgress() {
        normalizing = true;
        int overall = 0;
        for (Entry<CancelableTask, Integer> entry : cancelables.entrySet()) {
            overall += entry.getValue();
        }
        double factor = overall == 0 ? 1 : (double)100 / overall;
        for (Entry<CancelableTask, Integer> entry : cancelables.entrySet()) {
            entry.setValue((int) (entry.getValue() * factor));
        }
    }

    @Override
    protected void cancelImpl() {
        for (CancelableTask cancelable : cancelables.keySet()) {
            cancelable.cancel();
        }
    }

    @Override
    public int getProgress() {
        int progress = this.progress.get();
        int stateProgress = state.getProgress();
        this.progress.compareAndSet(progress, stateProgress); // Setting this value just for easier debugging. I has no meaning in CancelableCollection
        return super.getProgress();
    }

    private static class State {
        private CancelableTask currentTask;
        private int currentRelativeTime;
        private int progress;

        public State(CancelableTask currentTask, int currentRelativeTime,
                int progress) {
            super();
            this.currentTask = currentTask;
            this.currentRelativeTime = currentRelativeTime;
            this.progress = progress;
        }

        public int getProgress() {
            return progress
                    + (currentTask == null ? 0 : (int)(currentTask.getProgress()
                            * (double)currentRelativeTime / 100));
        }
    }
}
于 2012-12-24T18:10:31.247 回答
-1

停止您正在使用的线程或异步任务或调用 this.finish

于 2011-08-01T16:13:33.227 回答