在给定时间后等待并终止线程而不阻塞.NET 3.5
Waiting for and terminating a thread after a given time without blocking in .NET 3.5
我有一个 .NET 3.5 上的 WinForms 应用程序。在这种形式下,用户触发一个在另一个线程(准确地说是 BackgroundWorker
)中执行的操作,以免阻塞 UI 线程。我在 MVP 中,所以所有这一切都是由与视图界面交互的演示者完成的(由 Windows 表单实现)。到目前为止一切顺利。
我想介绍一种功能,即为后台操作引入超时时间,以便在取消操作之前完成。听起来很简单。但是后台操作调用第三方组件上的单个函数可能永远不会 return,所以 BackgroundWorker
的取消功能在这里对我没有用。此外,BackgroundWorker.RunWorkerCompleted
允许我回到 UI 线程,所以我需要等待超时或成功并能够回到我的调用线程(即 UI线程)。
我在第二个线程上使用普通的旧 Thread
(确实支持 Abort()
)和定时器 运行 进行了尝试,但似乎无法正常工作完全正确,因为 Join()
阻塞了我的 UI 线程,尽管描述表明它将阻塞 "while continuing to perform standard COM and SendMessage pumping"。诚然,我认为这意味着它将继续处理 Windows 消息,但事实并非如此。
int timeoutInMsec = 10000;
Thread connectThread = new Thread(Connect);
Thread timerThread = new Thread(() =>
{
var timer = new System.Windows.Forms.Timer() { Interval = timeoutInMsec };
timer.Tick += (_s, _e) =>
{
timer.Stop();
if (connectThread.ThreadState == ThreadState.Running)
connectThread.Abort();
};
};
connectThread.Start();
timerThread.Start();
timerThread.Join();
connectThread.Join();
基于 , I tried removing the second timer thread and adding a ManualResetEvent
and calling Set()
when the timer ticked, or when the Connect method did indeed complete. Here, instead of Join
I used WaitOne
, but unfortunately this also blocks my UI thread. I also found this other question,不幸的是 CancellationTokenSource
在 .NET 3.5 中不可用。
那么,如何在 .NET 3.5 中启动我的 worker 并能够在给定的时间后终止它,同时又能够返回到我启动 worker 的线程线程执行某种 OnCompleted
处理程序?
非常感谢!
PS: 我在 .NET 中的多线程编程方面经验不多,所以如果这很琐碎,我很抱歉。
在非gui线程上创建Forms.Timer
是没用的。不要在单独的线程上创建它。你为什么 Joining
线程? Join
的用法是阻塞当前线程,直到另一个线程结束。
这是未经测试的伪代码,仅供示例使用。
public class Form1: Form1
{
private int timeoutInMsec = 10000;
private System.Windows.Forms.Timer _timer;
private Thread _connectThread;
public Form1()
{
_connectThread = new Thread(Connect);
_connectThread.Start();
_timer = new System.Windows.Forms.Timer() { Interval = timeoutInMsec };
_timer.Tick += (_s, _e) =>
{
_timer.Stop();
if (_connectThread.ThreadState == ThreadState.Running)
_connectThread.Abort();
};
};
}
private void Connected()
{
}
private void Aborted()
{
}
private void Connect()
{
try
{
DoConnect3rdPartyStuff();
this.Invoke(Connected);
}
catch(ThreadAbortException)
{
// aborted
this.Invoke(Aborted);
}
}
}
如果我正确理解了你的问题,下面的算法应该可以解决你的问题:
和以前一样,创建一个 BackgroundWorker 来做你的后台工作。
在BackgroundWorker_DoWork,
- 创建一个新线程(我们称之为"third-party thread")来调用您的第三方库,然后
- 等待第三方线程完成或超时结束。 (*)
这样,您的 UI 就不会阻塞,因为 只有 Backgroundworker 线程 在等待,而不是主线程。
现在谈谈有趣的部分:如何等待第三方线程完成(标有 (*) 的步骤)?
我的建议是简单地使用"loop waiting with sleep",即(伪代码,你可以使用Stopwatch
class超时):
do until (third-party thread has finished or x seconds have elapsed):
Thread.Sleep for 100ms
if third-party thread has not finished:
Abort it // we don't have another choice
else
Process the result
这不是最佳实践,但它很简单,它可以完成工作,并且一旦一切正常,您总是可以用花哨的跨线程同步东西(这很重要)替换它。
我有一个 .NET 3.5 上的 WinForms 应用程序。在这种形式下,用户触发一个在另一个线程(准确地说是 BackgroundWorker
)中执行的操作,以免阻塞 UI 线程。我在 MVP 中,所以所有这一切都是由与视图界面交互的演示者完成的(由 Windows 表单实现)。到目前为止一切顺利。
我想介绍一种功能,即为后台操作引入超时时间,以便在取消操作之前完成。听起来很简单。但是后台操作调用第三方组件上的单个函数可能永远不会 return,所以 BackgroundWorker
的取消功能在这里对我没有用。此外,BackgroundWorker.RunWorkerCompleted
允许我回到 UI 线程,所以我需要等待超时或成功并能够回到我的调用线程(即 UI线程)。
我在第二个线程上使用普通的旧 Thread
(确实支持 Abort()
)和定时器 运行 进行了尝试,但似乎无法正常工作完全正确,因为 Join()
阻塞了我的 UI 线程,尽管描述表明它将阻塞 "while continuing to perform standard COM and SendMessage pumping"。诚然,我认为这意味着它将继续处理 Windows 消息,但事实并非如此。
int timeoutInMsec = 10000;
Thread connectThread = new Thread(Connect);
Thread timerThread = new Thread(() =>
{
var timer = new System.Windows.Forms.Timer() { Interval = timeoutInMsec };
timer.Tick += (_s, _e) =>
{
timer.Stop();
if (connectThread.ThreadState == ThreadState.Running)
connectThread.Abort();
};
};
connectThread.Start();
timerThread.Start();
timerThread.Join();
connectThread.Join();
基于 ManualResetEvent
and calling Set()
when the timer ticked, or when the Connect method did indeed complete. Here, instead of Join
I used WaitOne
, but unfortunately this also blocks my UI thread. I also found this other question,不幸的是 CancellationTokenSource
在 .NET 3.5 中不可用。
那么,如何在 .NET 3.5 中启动我的 worker 并能够在给定的时间后终止它,同时又能够返回到我启动 worker 的线程线程执行某种 OnCompleted
处理程序?
非常感谢!
PS: 我在 .NET 中的多线程编程方面经验不多,所以如果这很琐碎,我很抱歉。
在非gui线程上创建Forms.Timer
是没用的。不要在单独的线程上创建它。你为什么 Joining
线程? Join
的用法是阻塞当前线程,直到另一个线程结束。
这是未经测试的伪代码,仅供示例使用。
public class Form1: Form1
{
private int timeoutInMsec = 10000;
private System.Windows.Forms.Timer _timer;
private Thread _connectThread;
public Form1()
{
_connectThread = new Thread(Connect);
_connectThread.Start();
_timer = new System.Windows.Forms.Timer() { Interval = timeoutInMsec };
_timer.Tick += (_s, _e) =>
{
_timer.Stop();
if (_connectThread.ThreadState == ThreadState.Running)
_connectThread.Abort();
};
};
}
private void Connected()
{
}
private void Aborted()
{
}
private void Connect()
{
try
{
DoConnect3rdPartyStuff();
this.Invoke(Connected);
}
catch(ThreadAbortException)
{
// aborted
this.Invoke(Aborted);
}
}
}
如果我正确理解了你的问题,下面的算法应该可以解决你的问题:
和以前一样,创建一个 BackgroundWorker 来做你的后台工作。
在BackgroundWorker_DoWork,
- 创建一个新线程(我们称之为"third-party thread")来调用您的第三方库,然后
- 等待第三方线程完成或超时结束。 (*)
这样,您的 UI 就不会阻塞,因为 只有 Backgroundworker 线程 在等待,而不是主线程。
现在谈谈有趣的部分:如何等待第三方线程完成(标有 (*) 的步骤)?
我的建议是简单地使用"loop waiting with sleep",即(伪代码,你可以使用Stopwatch
class超时):
do until (third-party thread has finished or x seconds have elapsed):
Thread.Sleep for 100ms
if third-party thread has not finished:
Abort it // we don't have another choice
else
Process the result
这不是最佳实践,但它很简单,它可以完成工作,并且一旦一切正常,您总是可以用花哨的跨线程同步东西(这很重要)替换它。