停止/销毁线程

发布于 2024-11-01 22:41:58 字数 892 浏览 1 评论 0原文

我有一个像这样启动线程和可运行的服务。

t = new Thread(new Runnable() {
    public void run() {
        doSomething();
    }
});

t.start();

线程的原因是执行异步任务doSomething()。现在我们不用担心其他类 AsyncTask。我已经尝试过了,但它不适用于我的情况。编辑:我无法使用 AsyncTask,因为它仅适用于 UI 线程。这段代码必须在服务内部操作,所以不,没有 AsyncTask :(

doSomething() 包含一些外部库,所以我遇到的问题是它可能会挂在其中一个命令上,不返回任何值(因此甚至无法进行错误检查)

为了解决这个问题,我想在某个时候销毁该服务,

stopService(new Intent("net.MyService.intent));

这可以很好地在手机上进行验证。上面创建的将继续运行即使生成它的服务被销毁,

我也会寻找插入服务的 onDestroy() 的正确命令,这将为我清理线程,

t.destroy();
t.stop();

但这些命令都会被贬值并导致应用程序崩溃。

我从某处获取了这段代码

@Override
public void onDestroy() {

    Thread th = t;
    t = null;

    th.interrupt();

    super.onDestroy();
}

,但它仍然不起作用,线程继续运行,有人帮忙吗?

I have a Service that launches a Thread and a Runnable like so.

t = new Thread(new Runnable() {
    public void run() {
        doSomething();
    }
});

t.start();

The reason for the thread is to perform an Async task doSomething(). For now lets not worry about the other class AsyncTask. I have tried it and it does not work for my case. Edit: I can't use AsyncTask because it is meant for the UI thread only. This piece of code has to operate inside a Service, so nope, no AsyncTask :(

doSomething() contains some external libs so the issue I am having is that it can potentially be hung at one of the commands, without return any value (hence no error checking can even be done)

To work around this, I will want to, at some point of time, destroy the Service.

stopService(new Intent("net.MyService.intent));

This works fine and is easily verified on the phone. However, the Thread which was created above will continue to run even when the Service that spawned it is destroyed.

I am thus looking for the correct commands to insert in the Service's onDestroy() which will clean up the Thread for me.

t.destroy();
t.stop();

are both depreciated and cause application crashes.

I took this code from somewhere

@Override
public void onDestroy() {

    Thread th = t;
    t = null;

    th.interrupt();

    super.onDestroy();
}

but it still does not work, the thread continues to run. Any help guys?

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

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

发布评论

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

评论(7

叹倦 2024-11-08 22:41:58

线程destroystop 方法本质上容易发生死锁且不安全。它们的存在还给人一种错觉,即当其他线程告诉它时,可能有某种方法立即停止另一个线程。

我理解你的想法,从你的角度来看,它们是一个主线程,当这个线程一段时间内没有收到来自它的工作线程的响应时,你想杀死它并重新启动它,而不关心什么这取决于。但这些方法被弃用的原因是您应该关心线程在做什么。很多。

如果线程对稍后需要使用的变量有锁怎么办?如果线程打开了文件句柄怎么办?在所有这些情况以及更多情况下,简单地停止当前操作的线程会让事情变得一团糟——很可能您的应用程序会进一步崩溃。

因此,为了使线程可中断、可取消或可停止,它必须自行管理。如果线程或操作本身没有提供中断的方式,那么您就不能中断它 - 假设这样做是不安全的。

如果你确实可以运行

public void run() {
   doSomething();
}

,那么就没有办法中断它。人们希望,如果 doSomething 是一个很长的操作,那么可能有一种方法可以与它增量交互

public void run() {
   while (running) {
       MyParser.parseNext();
   }
}

,或者能够通过引用传递一个变量来指示线程是否被中断或不,希望该方法能够在合适的位置中断自身。

请记住,阻塞操作是阻塞的。没有办法解决这个问题,你不能中途取消它。

The thread destroy and stop methods are inherently deadlock prone and not safe. Their existence also gives the illusion that there might be some way of halting another thread immediately when something else tells it to.

I understand your thinking, from your point of view their is one main thread, and when this thread hasn't received a response from it's worker thread in a while you'd like to kill it and restart it, without caring what it's up to. But the reason those methods are deprecated is you should care what the thread is up to. A lot.

What if the thread has a lock around a variable you need to use later? What if a thread has a file handle open? In all these cases, and many more, simply stopping the thread at it's current operation would leave things in mess -- quite likely your application would just crash further down the line.

So in order for a thread to be interruptible or cancel-able or stoppable, it has to manage this itself. If a thread or operation provides no way for itself to be interrupted, then you cannot interrupt it - it is assumed to do so would be unsafe.

If you runnable is literally

public void run() {
   doSomething();
}

then there is no way to interrupt it. One would hope that if doSomething were a long operation that there might be a way to either interact with it incrementally with something like

public void run() {
   while (running) {
       MyParser.parseNext();
   }
}

or to be able to pass in a variable by reference which indicates whether the thread is interrupted or not, and hopefully the method would interrupt itself at suitable location.

Remember a blocking operation is blocking. There is no way to get around that, you cannot cancel it part way through.

桜花祭 2024-11-08 22:41:58

替代答案

使用以下代码:

MyThread thread;     // class field

像现在一样创建并启动线程。

thread = new MyThread();
thread.start();

当服务被销毁时,“信号”线程退出

public void onDestroy() {
    // Stop the thread
    thread.abort = true;
    thread.interrupt();
}

这是线程实现

//another class or maybe an inner class
class MyThread extends Thread {
    syncronized boolean abort = false;

    //ugly, I know
    public void run() {
       try {
           if(!abort) doA();
           if(!abort) doB();
           if(!abort) doC();
           if(!abort) doD();
       } catch (InterruptedException ex) {
          Log.w("tag", "Interrupted!");
       }
    }
}

您可能需要阅读以下内容:

我认为您可以依靠捕获异常而不检查中止,但我决定保持这种方式。

更新

我在代码大师

public class Worker implements Runnable {
    private String result;
    public run() {
        result = blockingMethodCall();
    }
    public String getResult() {
        return result;
    }
}

public class MainProgram {
    public void mainMethod() {
        ...
        Worker worker = new Worker(); 
        Thread thread = new Thread(worker); 
        thread.start();
        // Returns when finished executing, or after maximum TIME_OUT time
        thread.join(TIME_OUT); 
        if (thread.isAlive()) {
            // If the thread is still alive, it's still blocking on the methodcall, try stopping it
            thread.interrupt();
            return null;
        } else {
            // The thread is finished, get the result
            return worker.getResult(); 
        }
    }
}

Alternative answer

Use the following code:

MyThread thread;     // class field

Create and start the thread as you do it right now.

thread = new MyThread();
thread.start();

When the service is destroyed, "signal" the thread to quit

public void onDestroy() {
    // Stop the thread
    thread.abort = true;
    thread.interrupt();
}

Here is thread implementation

//another class or maybe an inner class
class MyThread extends Thread {
    syncronized boolean abort = false;

    //ugly, I know
    public void run() {
       try {
           if(!abort) doA();
           if(!abort) doB();
           if(!abort) doC();
           if(!abort) doD();
       } catch (InterruptedException ex) {
          Log.w("tag", "Interrupted!");
       }
    }
}

You might want to read the following:

I think that you could rely on catching the exception and not check abort but I decided to keep it that way.

UPDATE

I've seen this sample in codeguru:

public class Worker implements Runnable {
    private String result;
    public run() {
        result = blockingMethodCall();
    }
    public String getResult() {
        return result;
    }
}

public class MainProgram {
    public void mainMethod() {
        ...
        Worker worker = new Worker(); 
        Thread thread = new Thread(worker); 
        thread.start();
        // Returns when finished executing, or after maximum TIME_OUT time
        thread.join(TIME_OUT); 
        if (thread.isAlive()) {
            // If the thread is still alive, it's still blocking on the methodcall, try stopping it
            thread.interrupt();
            return null;
        } else {
            // The thread is finished, get the result
            return worker.getResult(); 
        }
    }
}
绾颜 2024-11-08 22:41:58

您是否检查了 Java 线程基元弃用文档Thread API JavaDoc 中引用了它。您会发现一些提示来解决您的问题。

Did you check the Java Thread Primitive Deprecation Documentation which is referenced in the Thread API JavaDoc. You will find some hints to handle your problem.

锦上情书 2024-11-08 22:41:58

为什么不使用 AsyncTask

任务可以随时取消
调用取消(布尔值)。调用
该方法会导致后续
调用 isCancelled() 返回 true。
调用该方法后,
onCancelled(Object),而不是
onPostExecute(Object) 将被调用
在 doInBackground(Object[]) 之后
返回。为了确保任务是
尽快取消,你
应始终检查返回值
isCancelled() 定期从
doInBackground(Object[]),如果可能的话
(例如在循环内。)

why don't you use an AsyncTask?

A task can be cancelled at any time by
invoking cancel(boolean). Invoking
this method will cause subsequent
calls to isCancelled() to return true.
After invoking this method,
onCancelled(Object), instead of
onPostExecute(Object) will be invoked
after doInBackground(Object[])
returns. To ensure that a task is
cancelled as quickly as possible, you
should always check the return value
of isCancelled() periodically from
doInBackground(Object[]), if possible
(inside a loop for instance.)

独孤求败 2024-11-08 22:41:58

我喜欢采取以下方法:

class MyHandler extends Handler {
    final Semaphore stopEvent = new Semaphore(0);

    @Override
    public void handleMessage(Message msg) {
        try {
            while (!stopEvent.tryAcquire(0, TimeUnit.SECONDS)) {
                doSomething();

                if (stopEvent.tryAcquire(SLEEP_TIME, TimeUnit.MILLISECONDS)) {
                    break;
                }
            }
        } catch (InterruptedException ignored) {
        }

        stopSelf();
    }
}

在服务 onDestroy 上只需释放 stopEvent:

@Override
public void onDestroy() {
    myHandler.stopEvent.release();
    myHandler = null;

    super.onDestroy();
}

I like to take the following approach:

class MyHandler extends Handler {
    final Semaphore stopEvent = new Semaphore(0);

    @Override
    public void handleMessage(Message msg) {
        try {
            while (!stopEvent.tryAcquire(0, TimeUnit.SECONDS)) {
                doSomething();

                if (stopEvent.tryAcquire(SLEEP_TIME, TimeUnit.MILLISECONDS)) {
                    break;
                }
            }
        } catch (InterruptedException ignored) {
        }

        stopSelf();
    }
}

On service onDestroy just release the stopEvent:

@Override
public void onDestroy() {
    myHandler.stopEvent.release();
    myHandler = null;

    super.onDestroy();
}
乖乖兔^ω^ 2024-11-08 22:41:58

最好使用全局变量 stopThread,一旦变量更改为 true 就停止线程。

btnStop.setOnClickListener(new OnClickListener() {          
        @Override
        public void onClick(View arg0){
            stopThread = true;
        }
    }); 


public void run() {
        while (!stopThread) {
           //do something
        }
}

Better to use global variable stopThread, stop thread once variable changed to true.

btnStop.setOnClickListener(new OnClickListener() {          
        @Override
        public void onClick(View arg0){
            stopThread = true;
        }
    }); 


public void run() {
        while (!stopThread) {
           //do something
        }
}
月野兔 2024-11-08 22:41:58

我认为创建另一个线程并与另一个线程通信的最佳方法是使用 AsyncTask< /a>.下面是其中的一个示例:

public class Task extends AsyncTask<Void, Void, Void> {

    private static final String TAG = "Task";

    private boolean mPaused;

    private Runnable mRunnable;

    public Task(Runnable runnable) {
        mRunnable = runnable;
        play();
    }

    @Override
    protected Void doInBackground(Void... params) {
        while (!isCancelled()) {
            if (!mPaused) {

                mRunnable.run();
                sleep();
            }
        }
        return null;
    }

    private void sleep() {
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            Log.w(TAG, e.getMessage());
        }
    }

    public void play() {
        mPaused = false;
    }

    public void pause() {
        mPaused = true;
    }

    public void stop() {
        pause();
        cancel(true);
    }

    public boolean isPaused() {
        return mPaused;
    }

}

您现在可以轻松地使用此类,并通过编写来启动线程:

Task task = new Task(myRunnable);
task.execute((Void) null);

除此之外,您可以轻松地暂停或停止线程循环:

暂停和播放线程的示例:

mButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        if (task.isPaused()) {
            task.play();
        } else {
            task.pause();
        }
    }
});

停止和启动线程的示例:

mButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        if (task.isCancelled()) {
            task = new Task(myRunnable);
            task.execute((Void) null);
        } else {
            task.stop();
        }
    }
});

I think the best way to create and communicate with another thread is to use an AsyncTask. Heres an example of one:

public class Task extends AsyncTask<Void, Void, Void> {

    private static final String TAG = "Task";

    private boolean mPaused;

    private Runnable mRunnable;

    public Task(Runnable runnable) {
        mRunnable = runnable;
        play();
    }

    @Override
    protected Void doInBackground(Void... params) {
        while (!isCancelled()) {
            if (!mPaused) {

                mRunnable.run();
                sleep();
            }
        }
        return null;
    }

    private void sleep() {
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            Log.w(TAG, e.getMessage());
        }
    }

    public void play() {
        mPaused = false;
    }

    public void pause() {
        mPaused = true;
    }

    public void stop() {
        pause();
        cancel(true);
    }

    public boolean isPaused() {
        return mPaused;
    }

}

You can now easily use this class, and start the thread by writing:

Task task = new Task(myRunnable);
task.execute((Void) null);

Along with this you can easily pause or stop the thread from looping:

Example of pausing and playing the thread:

mButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        if (task.isPaused()) {
            task.play();
        } else {
            task.pause();
        }
    }
});

Example of stopping and starting the thread:

mButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        if (task.isCancelled()) {
            task = new Task(myRunnable);
            task.execute((Void) null);
        } else {
            task.stop();
        }
    }
});
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文