如何才能停止我正在运行的代码?

发布于 2024-11-27 03:20:21 字数 199 浏览 1 评论 0原文

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

如何实现这一点?有这方面的设计模式吗?

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

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

I have a long operation running in the background, like uploading stuff, converting images, audio, video, etc.
I would like to stop/cancel them if the user requested to stop the operation altogether.

How can accomplish this? Is there a design pattern for this?

Note: Some of the running code can be canceled and some can't. How do I find a compromise around that?

EDIT: I should have said that I want the operation to stop immediately.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

梦幻的心爱 2024-12-04 03:20:22

总结和扩展乔恩所说的:

  • 您应该让线程知道它应该退出循环(易失性标志)。
  • 如果您希望线程退出阻塞状态,您可以 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
        }
    }
}

To summarize and extend on what Jon has said:

  • You should let the thread know that it should exit the loop (volatile flag).
  • You may interrupt() the thread if you want it to exit out of a blocking state.
  • You should handle the InterruptedException inside the run method.
  • You should exit gracefully when you're interrupted (i.e. finish up whatever you're doing and clean up).

Some code:

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
        }
    }
}
醉生梦死 2024-12-04 03:20:22

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

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

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

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

(I'm assuming you're already performing the background work in a separate thread.)

Basically, you keep a shared boolean flag which the UI thread can set and the background thread periodically reads. When the flag says "stop", you stop :)

Note that the flag should be volatile or you should use a lock in order to make sure that the background thread definitely "sees" a change written from the UI thread.

It's relatively crude and feels a bit "manual" but it means you don't risk instability through aborting half way through an operation, unlike approaches such as Thread.stop().

高冷爸爸 2024-12-04 03:20:22

我的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));
        }
    }
}

My 2 cents. A task that is cancelable. The cancelImpl() and runImpl() need to be implemented.

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;
//  }
}

There is the CancelableCollection as well:

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));
        }
    }
}
泪之魂 2024-12-04 03:20:22

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

stop the thread or asynctask youre using or call this.finish

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文