如何在线程之外停止定时任务/线程。

huangapple go评论135阅读模式
英文:

How to stop scheduled task/thread outside of thread

问题

I'm trying to practice and learn more about multi-threading and scheduling tasks.
我正在尝试练习并学习更多关于多线程和任务调度的知识。

I wrote a test program that mimics a scheduler I'm trying to implement in a bot and its behaving in a way I don't really understand.
我编写了一个测试程序,模拟了我正在尝试在机器人中实现的调度程序,但它的行为方式让我感到困惑。

Basically I created a task and scheduled it to run and I want it to be canceled after some event (in this instance, when count > 5).
基本上,我创建了一个任务并安排它运行,我希望在某个事件发生后取消它(在这种情况下,当count > 5时)。

It seems to run indefinitely even though the count is over 5, but when I put in a line to sleep the main thread or print from it, it works as I'd expect it to.
尽管计数超过5,但它似乎会无限运行,但当我添加一行代码来使主线程休眠或从主线程中打印信息时,它的行为就像我预期的那样。

Can someone why this is the case?
有人可以解释为什么会出现这种情况吗?

It's as if like if there is no interaction with the main thread, it never hits the condition or just ignores it, but as soon as I put something in for the main thread to process, it checks the condition as well.
就好像如果没有与主线程的交互,它就永远不会达到条件,或者只是忽略它,但一旦我为主线程添加了一些处理内容,它也会检查条件。

Here is the output with Thread.sleep and println commented out:
以下是将Thread.sleep和println注释掉的输出结果:

And with the 2 lines uncommented:
取消注释后的输出:

If there are any resources to look at that can explain why the above scenario occurs and possibly one for an intro to multithreading I'd appreciate it.
如果有任何资源可以解释上述情况发生的原因,并且可能有一个多线程介绍的资源,我将不胜感激。

Also, is there an ideal way to handle cancelling threads outside of the thread in question, like having one dedicated to checking/managing the conditions?
此外,是否有一种理想的方式来处理除了相关线程之外的线程取消,例如有一个专门用于检查/管理条件的线程?

英文:

I'm trying to practice and learn more about multi-threading and scheduling tasks.
I wrote a test program that mimics a scheduler I'm trying to implement in a bot and its behaving in a way I don't really understand. Basically I created a task and scheduled it to run and I want it to be canceled after some event (in this instance, when count > 5).
It seems to run indefinitely even though the count is over 5, but when I put in a line to sleep the main thread or print from it, it works as I'd expect it to.

Can someone why this is the case?
It's as if like if there is no interaction with the main thread, it never hits the condition or just ignores it, but as soon as I put something in for the main thread to process, it checks the condition as well.

public class Driver {
  public static void main(String[] args) throws InterruptedException {
    TestScheduler test = new TestScheduler();
    test.startScheduler();
  }
}


public class TestScheduler {
  private static ScheduledExecutorService ses;
  private static int count;

  public TestScheduler(){
    ses = Executors.newScheduledThreadPool(2);
    count = 0;
  }

  public void startScheduler() throws InterruptedException {
    System.out.println("startScheduler() thread: " + Thread.currentThread().getName());

    Runnable testTask = () -> {
      System.out.println(Thread.currentThread().getName() + ": count " + count++);
    };

    System.out.println("Starting test scheduler for 10s");
    ScheduledFuture<?> scheduledFuture = ses.scheduleAtFixedRate(testTask, 5, 1, TimeUnit.SECONDS);
    System.out.println("ScheduledFuture started...");

    while(true){
      // if any of the 2 lines below are uncommented, it works as I'd expect it to...
      //Thread.sleep(1000);
      //System.out.println(Thread.currentThread().getName() + ": count " + count);
      if (count > 5){
        System.out.println(Thread.currentThread().getName() + ": Cancelling scheduled task.");
        scheduledFuture.cancel(true);
        break;
      }
    }
    System.out.println("Ending test scheduler");
  }

Here is the output with Thread.sleep and println commented out:

startScheduler() thread: main
Starting test scheduler for 10s
ScheduledFuture started...
pool-1-thread-1: count 0
pool-1-thread-2: count 1
pool-1-thread-2: count 2
pool-1-thread-2: count 3
pool-1-thread-2: count 4
pool-1-thread-2: count 5
pool-1-thread-2: count 6
pool-1-thread-2: count 7
pool-1-thread-1: count 8
pool-1-thread-1: count 9
pool-1-thread-1: count 10
...

And with the 2 lines uncommented:

startScheduler() thread: main
Starting test scheduler for 10s
ScheduledFuture started...
main: count 0
main: count 0
main: count 0
main: count 0
pool-1-thread-1: count 0
main: count 1
pool-1-thread-1: count 1
main: count 2
pool-1-thread-1: count 2
main: count 3
pool-1-thread-1: count 3
main: count 4
pool-1-thread-1: count 4
main: count 5
pool-1-thread-1: count 5
main: count 6
main: Cancelling scheduled task.
Ending test scheduler

If there are any resources to look at that can explain why the above scenario occurs and possibly one for an intro to multithreading I'd appreciate it.

Also, is there an ideal way to handle cancelling threads outside of the thread in question, like having one dedicated to checking/managing the conditions?

答案1

得分: 1

这是由于在访问 count 时出现了竞态条件
两个线程在没有任何锁的情况下同时访问了这个变量。
你可以使用 AtomicInteger 来解决这个问题:

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public class Driver {
  public static void main(String[] args) throws InterruptedException {
    TestScheduler test = new TestScheduler();
    test.startScheduler();
  }
}

class TestScheduler {
  private ScheduledExecutorService ses = Executors.newScheduledThreadPool(2);
  private AtomicInteger count = new AtomicInteger(0);

  public void startScheduler() throws InterruptedException {
    System.out.println("startScheduler() thread: " + Thread.currentThread().getName());

    Runnable testTask = () -> {
      System.out.println(Thread.currentThread().getName() + ": count " + count.getAndIncrement());
    };

    System.out.println("Starting test scheduler for 10s");
    ScheduledFuture<?> scheduledFuture = ses.scheduleAtFixedRate(testTask, 5, 1, TimeUnit.SECONDS);
    System.out.println("ScheduledFuture started...");

    while(true){
      if (count.get() > 5){
        System.out.println(Thread.currentThread().getName() + ": Cancelling scheduled task.");
        scheduledFuture.cancel(true);
        break;
      }
    }
    System.out.println("Ending test scheduler");
  }
}
英文:

Its happening due to race conditions while accessing count.
2 threads are accessing this variable at the same time without any locks.
You can use an AtomicInteger to overcome this:

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public class Driver {
  public static void main(String[] args) throws InterruptedException {
    TestScheduler test = new TestScheduler();
    test.startScheduler();
  }
}


class TestScheduler {
  private ScheduledExecutorService ses = Executors.newScheduledThreadPool(2);
  private AtomicInteger count = new AtomicInteger(0);

  public void startScheduler() throws InterruptedException {
    System.out.println(&quot;startScheduler() thread: &quot; + Thread.currentThread().getName());

    Runnable testTask = () -&gt; {
      System.out.println(Thread.currentThread().getName() + &quot;: count &quot; + count.getAndIncrement());
    };

    System.out.println(&quot;Starting test scheduler for 10s&quot;);
    ScheduledFuture&lt;?&gt; scheduledFuture = ses.scheduleAtFixedRate(testTask, 5, 1, TimeUnit.SECONDS);
    System.out.println(&quot;ScheduledFuture started...&quot;);

    while(true){
      if (count.get() &gt; 5){
        System.out.println(Thread.currentThread().getName() + &quot;: Cancelling scheduled task.&quot;);
        scheduledFuture.cancel(true);
        break;
      }
    }
    System.out.println(&quot;Ending test scheduler&quot;);
  }
}

答案2

得分: -1

以下是您提供的代码的中文翻译部分:

实际上,原因是多线程使用了不同的 CPU 核心,所以同一个变量在不同的 CPU 缓存中保留了不同的值,您可以将 count 声明为 volatile 来解决这个问题。如果您对 volatile 感兴趣,可以查看这篇文章:http://tutorials.jenkov.com/java-concurrency/volatile.html。以下是代码:

package com.test;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

/**
 *
 */
public class TestList {

    public static class TestScheduler {
        private static ScheduledExecutorService ses;
        private static volatile int count;

        public TestScheduler() {
            ses = Executors.newScheduledThreadPool(2);
            count = 0;
        }

        public void startScheduler() throws InterruptedException {
            System.out.println("startScheduler() 线程:" + Thread.currentThread().getName());

            Runnable testTask = () -> {
                System.out.println(Thread.currentThread().getName() + ":count " + count++);
            };

            System.out.println("开始测试调度器,持续 10 秒");
            ScheduledFuture<?> scheduledFuture = ses.scheduleWithFixedDelay(testTask, 5, 1, TimeUnit.SECONDS);
            System.out.println("ScheduledFuture 已启动...");

            while (true) {
                // 如果取消下面的任意一行的注释,它会按照我的预期工作...
                // Thread.sleep(1000);
                // System.out.println(Thread.currentThread().getName() + ":count " + count);
                if (count > 5) {

                    System.out.println(Thread.currentThread().getName() + ":取消预定任务。");
                    scheduledFuture.cancel(true);
                    break;
                }
            }
            System.out.println("结束测试调度器");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        TestScheduler test = new TestScheduler();
        test.startScheduler();
    }
}
英文:

actually the reason is that the multi thread has used the differect cpu core,so the same variable keep different value in the different cpu cache,you could just make the count to volatile to solve the problem.You could see the post http://tutorials.jenkov.com/java-concurrency/volatile.html if you are interesting about the volatile .The code is that

package com.test;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
/**
*
*/
public class TestList {
public static class TestScheduler {
private static ScheduledExecutorService ses;
private static volatile int count;
public TestScheduler() {
ses = Executors.newScheduledThreadPool(2);
count = 0;
}
public void startScheduler() throws InterruptedException {
System.out.println(&quot;startScheduler() thread: &quot; + Thread.currentThread().getName());
Runnable testTask = () -&gt; {
System.out.println(Thread.currentThread().getName() + &quot;: count &quot; + count++);
};
System.out.println(&quot;Starting test scheduler for 10s&quot;);
ScheduledFuture&lt;?&gt; scheduledFuture = ses.scheduleWithFixedDelay(testTask, 5, 1, TimeUnit.SECONDS);
System.out.println(&quot;ScheduledFuture started...&quot;);
while (true) {
// if any of the 2 lines below are uncommented, it works as I&#39;d expect it to...
// Thread.sleep(1000);
// System.out.println(Thread.currentThread().getName() + &quot;: count &quot; + count);
if (count &gt; 5) {
System.out.println(Thread.currentThread().getName() + &quot;: Cancelling scheduled task.&quot;);
scheduledFuture.cancel(true);
break;
}
}
System.out.println(&quot;Ending test scheduler&quot;);
}
}
public static void main(String[] args) throws InterruptedException {
TestScheduler test = new TestScheduler();
test.startScheduler();
}
}

huangapple
  • 本文由 发表于 2020年7月23日 09:59:01
  • 转载请务必保留本文链接:https://go.coder-hub.com/63045720.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定