从定时器事件中止线程
Aborting thread from timer event
我有一个 Timer
如果它需要太多时间就必须取消 Thread
。
System.Timers.Timer timer_timers = new System.Timers.Timer();
Thread thread = new Thread(startJob);
thread.Name = "VICTIM_THREAD";
启动 Thread
方法时,我启动 Timer
并将当前线程作为参数传递给事件。
public void startJob()
{
Debug.WriteLine("Name: " + Thread.CurrentThread.Name);
timer_timers.Elapsed += (sender, e) => T_Elapsed(sender, e, Thread.CurrentThread);
timer_timers.Interval = 5000;
// Start simulation process
while (true)
{
Thread.Sleep(700);
Debug.WriteLine("Thread: " + Thread.CurrentThread.Name + " ALIVE: " + thread.IsAlive);
}
}
计时器事件:
private void T_Elapsed(object sender, ElapsedEventArgs e, Thread currentThread)
{
// EDIT: show the correct NAME! of the thread
Debug.WriteLine("Name: " + currentThread.Name);
System.Timers.Timer tim = sender as System.Timers.Timer;
currentThread.Abort(); // <-- this line throws exception
if (tim != null)
{
tim.Stop();
}
}
但是 Abort
调用抛出异常:
'Unable to evaluate expression because the code is optimized or a native frame is on top of the call stack'
并且线程保持活动状态。
如果我在 startJob()
之前启动计时器并直接传递线程,它就可以正常工作。
public void startThread()
{
timer_timers.Elapsed += (sender, e) => T_Elapsed(sender, e, thread);
timer_timers.Interval = 5000;
timer_timers.Start();
thread.Start();
}
public void startJob()
{
// Start simulation process
while (true)
{
Thread.Sleep(700);
Debug.WriteLine("Thread: " + Thread.CurrentThread.Name + " ALIVE: " + thread.IsAlive);
}
}
问题:为什么Thread.CurrentThread
版本不能用?是因为我还必须中止计时器线程吗?我在这里错过了什么?
我发现像 this and this 这样的异常的答案来自不同的上下文,并不能真正帮助我理解确切的原因。
编辑:我知道这是中止或取消线程的错误方法。它应该做的工作是打开一个 SerialPort。但是每 ~200 次线程就永远不会 return 一次,我需要杀死它而不管后果。模拟 while 循环可能是一个不好的例子。
如评论中所述,您不应使用 Abort
。即使你这样做了,这也是你使用它的方式的问题:
计时器不会 运行 您的线程。他们 运行 在线程池线程上。因此,在您的 lambda 中使用的 Thread.CurrentThread
将成为该线程池线程。
如果您想中止正在创建计时器的线程,您应该这样做:在 lambda 之外的变量中捕获线程。
Thread myThread = Thread.CurrentThread;
timer_timers.Elapsed += (sender, e) => T_Elapsed(sender, e, myThread);
但是您应该找到另一种方法来更优雅地终止您的线程,或者重新编写您的代码以不需要显式线程。
永远不要打电话给 Thread.Abort
。以下是正确的做法:
var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
var t = Task.Run(() =>
{
while (!token.IsCancellationRequested)
{
Console.Write(".");
Thread.Sleep(500);
}
}, token);
var timer = new System.Timers.Timer();
timer.Interval = 5000;
timer.Elapsed += (s, e) => tokenSource.Cancel();
timer.Enabled = true;
您的代码在第二种情况下似乎有效的原因是您在调用 T_Elapsed
之前捕获了线程。在第一种情况下,您仅在调用时间 Elapsed
事件时才请求当前线程(此时它不是调用线程,而是被调用者)。
我有一个 Timer
如果它需要太多时间就必须取消 Thread
。
System.Timers.Timer timer_timers = new System.Timers.Timer();
Thread thread = new Thread(startJob);
thread.Name = "VICTIM_THREAD";
启动 Thread
方法时,我启动 Timer
并将当前线程作为参数传递给事件。
public void startJob()
{
Debug.WriteLine("Name: " + Thread.CurrentThread.Name);
timer_timers.Elapsed += (sender, e) => T_Elapsed(sender, e, Thread.CurrentThread);
timer_timers.Interval = 5000;
// Start simulation process
while (true)
{
Thread.Sleep(700);
Debug.WriteLine("Thread: " + Thread.CurrentThread.Name + " ALIVE: " + thread.IsAlive);
}
}
计时器事件:
private void T_Elapsed(object sender, ElapsedEventArgs e, Thread currentThread)
{
// EDIT: show the correct NAME! of the thread
Debug.WriteLine("Name: " + currentThread.Name);
System.Timers.Timer tim = sender as System.Timers.Timer;
currentThread.Abort(); // <-- this line throws exception
if (tim != null)
{
tim.Stop();
}
}
但是 Abort
调用抛出异常:
'Unable to evaluate expression because the code is optimized or a native frame is on top of the call stack'
并且线程保持活动状态。
如果我在 startJob()
之前启动计时器并直接传递线程,它就可以正常工作。
public void startThread()
{
timer_timers.Elapsed += (sender, e) => T_Elapsed(sender, e, thread);
timer_timers.Interval = 5000;
timer_timers.Start();
thread.Start();
}
public void startJob()
{
// Start simulation process
while (true)
{
Thread.Sleep(700);
Debug.WriteLine("Thread: " + Thread.CurrentThread.Name + " ALIVE: " + thread.IsAlive);
}
}
问题:为什么Thread.CurrentThread
版本不能用?是因为我还必须中止计时器线程吗?我在这里错过了什么?
我发现像 this and this 这样的异常的答案来自不同的上下文,并不能真正帮助我理解确切的原因。
编辑:我知道这是中止或取消线程的错误方法。它应该做的工作是打开一个 SerialPort。但是每 ~200 次线程就永远不会 return 一次,我需要杀死它而不管后果。模拟 while 循环可能是一个不好的例子。
如评论中所述,您不应使用 Abort
。即使你这样做了,这也是你使用它的方式的问题:
计时器不会 运行 您的线程。他们 运行 在线程池线程上。因此,在您的 lambda 中使用的 Thread.CurrentThread
将成为该线程池线程。
如果您想中止正在创建计时器的线程,您应该这样做:在 lambda 之外的变量中捕获线程。
Thread myThread = Thread.CurrentThread;
timer_timers.Elapsed += (sender, e) => T_Elapsed(sender, e, myThread);
但是您应该找到另一种方法来更优雅地终止您的线程,或者重新编写您的代码以不需要显式线程。
永远不要打电话给 Thread.Abort
。以下是正确的做法:
var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
var t = Task.Run(() =>
{
while (!token.IsCancellationRequested)
{
Console.Write(".");
Thread.Sleep(500);
}
}, token);
var timer = new System.Timers.Timer();
timer.Interval = 5000;
timer.Elapsed += (s, e) => tokenSource.Cancel();
timer.Enabled = true;
您的代码在第二种情况下似乎有效的原因是您在调用 T_Elapsed
之前捕获了线程。在第一种情况下,您仅在调用时间 Elapsed
事件时才请求当前线程(此时它不是调用线程,而是被调用者)。