我在后台运行了很长时间的操作,例如上传内容、转换图像、音频、视频等。如果用户要求完全停止操作,我想停止/取消它们。
怎么能做到这一点?这有设计模式吗?
注意:有些运行代码可以取消,有些不能。我如何找到一个折衷方案?
编辑:我应该说我希望操作立即停止。
我在后台运行了很长时间的操作,例如上传内容、转换图像、音频、视频等。如果用户要求完全停止操作,我想停止/取消它们。
怎么能做到这一点?这有设计模式吗?
注意:有些运行代码可以取消,有些不能。我如何找到一个折衷方案?
编辑:我应该说我希望操作立即停止。
总结和扩展乔恩所说的话:
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
}
}
}
(我假设您已经在单独的线程中执行后台工作。)
boolean
基本上,您保留一个UI 线程可以设置的共享标志,并且后台线程会定期读取。当旗帜说“停止”时,你停止:)
请注意,该标志应该是易失的,或者您应该使用锁以确保后台线程肯定“看到”从 UI 线程写入的更改。
它相对粗糙,感觉有点“手动”,但这意味着您不会因为中途中止操作而冒着不稳定的风险,这与Thread.stop()
.
我的 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));
}
}
}
停止您正在使用的线程或异步任务或调用 this.finish